summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/crossbuilds.yml8
-rw-r--r--MAINTAINERS9
-rw-r--r--VERSION2
-rw-r--r--block.c1327
-rw-r--r--block/backup-top.c48
-rw-r--r--block/block-backend.c30
-rw-r--r--block/commit.c1
-rw-r--r--block/file-posix.c91
-rw-r--r--block/io.c31
-rw-r--r--block/mirror.c3
-rw-r--r--blockdev.c4
-rw-r--r--blockjob.c11
-rw-r--r--bsd-user/bsdload.c55
-rw-r--r--bsd-user/qemu.h4
-rw-r--r--bsd-user/syscall.c1
-rw-r--r--docs/devel/decodetree.rst11
-rw-r--r--docs/sphinx/qapidoc.py3
-rw-r--r--fpu/softfloat-specialize.c.inc3
-rw-r--r--hw/arm/smmuv3.c6
-rw-r--r--hw/arm/virt.c7
-rw-r--r--hw/block/vhost-user-blk.c5
-rw-r--r--hw/core/machine.c5
-rw-r--r--hw/i386/pc.c3
-rw-r--r--hw/i386/pc_piix.c14
-rw-r--r--hw/i386/pc_q35.c13
-rw-r--r--hw/isa/piix4.c5
-rw-r--r--hw/mips/meson.build9
-rw-r--r--hw/pci-host/gpex.c56
-rw-r--r--hw/ppc/spapr.c15
-rw-r--r--hw/s390x/s390-virtio-ccw.c14
-rw-r--r--include/block/block.h14
-rw-r--r--include/block/block_int.h8
-rw-r--r--include/exec/helper-gen.h4
-rw-r--r--include/exec/helper-proto.h4
-rw-r--r--include/exec/helper-tcg.h4
-rw-r--r--include/hw/boards.h3
-rw-r--r--include/hw/i386/pc.h3
-rw-r--r--include/hw/pci-host/gpex.h4
-rw-r--r--include/qemu/transactions.h63
-rw-r--r--linux-user/hexagon/cpu_loop.c2
-rw-r--r--meson.build7
-rw-r--r--qemu-img.c2
-rw-r--r--scripts/decodetree.py172
-rw-r--r--scripts/qapi/error.py47
-rw-r--r--scripts/qapi/expr.py442
-rw-r--r--scripts/qapi/mypy.ini10
-rw-r--r--scripts/qapi/parser.py14
-rw-r--r--scripts/qapi/pylintrc4
-rw-r--r--scripts/qapi/schema.py4
-rw-r--r--target/arm/cpu.h105
-rw-r--r--target/arm/helper-a64.c2
-rw-r--r--target/arm/helper-a64.h3
-rw-r--r--target/arm/helper.c162
-rw-r--r--target/arm/internals.h13
-rw-r--r--target/arm/mte_helper.c181
-rw-r--r--target/arm/neon-ls.decode4
-rw-r--r--target/arm/sve_helper.c100
-rw-r--r--target/arm/translate-a64.c236
-rw-r--r--target/arm/translate-a64.h2
-rw-r--r--target/arm/translate-neon.c.inc117
-rw-r--r--target/arm/translate-sve.c11
-rw-r--r--target/arm/translate-vfp.c.inc20
-rw-r--r--target/arm/translate.c270
-rw-r--r--target/arm/translate.h38
-rw-r--r--target/hexagon/arch.c181
-rw-r--r--target/hexagon/arch.h9
-rw-r--r--target/hexagon/conv_emu.c177
-rw-r--r--target/hexagon/conv_emu.h31
-rw-r--r--target/hexagon/cpu.c14
-rw-r--r--target/hexagon/cpu.h5
-rw-r--r--target/hexagon/cpu_bits.h2
-rw-r--r--target/hexagon/decode.c84
-rw-r--r--target/hexagon/fma_emu.c40
-rw-r--r--target/hexagon/gen_tcg.h424
-rwxr-xr-xtarget/hexagon/gen_tcg_funcs.py2
-rw-r--r--target/hexagon/genptr.c233
-rw-r--r--target/hexagon/helper.h23
-rw-r--r--target/hexagon/iclass.c4
-rw-r--r--target/hexagon/imported/alu.idef44
-rw-r--r--target/hexagon/imported/compare.idef12
-rw-r--r--target/hexagon/imported/encode_pp.def30
-rw-r--r--target/hexagon/imported/float.idef32
-rw-r--r--target/hexagon/imported/ldst.idef68
-rwxr-xr-xtarget/hexagon/imported/macros.def47
-rw-r--r--target/hexagon/imported/shift.idef47
-rw-r--r--target/hexagon/insn.h21
-rw-r--r--target/hexagon/internal.h11
-rw-r--r--target/hexagon/macros.h118
-rw-r--r--target/hexagon/meson.build1
-rw-r--r--target/hexagon/op_helper.c392
-rw-r--r--target/hexagon/reg_fields.c3
-rw-r--r--target/hexagon/reg_fields.h4
-rw-r--r--target/hexagon/translate.c175
-rw-r--r--target/hexagon/translate.h9
-rw-r--r--target/mips/cpu.c281
-rw-r--r--target/mips/fpu.c25
-rw-r--r--target/mips/helper.h183
-rw-r--r--target/mips/internal.h107
-rw-r--r--target/mips/meson.build53
-rw-r--r--target/mips/msa.c60
-rw-r--r--target/mips/op_helper.c1210
-rw-r--r--target/mips/sysemu/addr.c (renamed from target/mips/addr.c)0
-rw-r--r--target/mips/sysemu/cp0.c123
-rw-r--r--target/mips/sysemu/cp0_timer.c (renamed from target/mips/cp0_timer.c)0
-rw-r--r--target/mips/sysemu/machine.c (renamed from target/mips/machine.c)21
-rw-r--r--target/mips/sysemu/meson.build7
-rw-r--r--target/mips/sysemu/physaddr.c257
-rw-r--r--target/mips/tcg/dsp_helper.c (renamed from target/mips/dsp_helper.c)0
-rw-r--r--target/mips/tcg/exception.c167
-rw-r--r--target/mips/tcg/fpu_helper.c (renamed from target/mips/fpu_helper.c)8
-rw-r--r--target/mips/tcg/ldst_helper.c288
-rw-r--r--target/mips/tcg/lmmi_helper.c (renamed from target/mips/lmmi_helper.c)0
-rw-r--r--target/mips/tcg/meson.build35
-rw-r--r--target/mips/tcg/mips32r6.decode (renamed from target/mips/mips32r6.decode)0
-rw-r--r--target/mips/tcg/mips64r6.decode (renamed from target/mips/mips64r6.decode)0
-rw-r--r--target/mips/tcg/msa32.decode (renamed from target/mips/msa32.decode)0
-rw-r--r--target/mips/tcg/msa64.decode (renamed from target/mips/msa64.decode)0
-rw-r--r--target/mips/tcg/msa_helper.c (renamed from target/mips/msa_helper.c)36
-rw-r--r--target/mips/tcg/msa_helper.h.inc (renamed from target/mips/msa_helper.h.inc)0
-rw-r--r--target/mips/tcg/msa_translate.c (renamed from target/mips/msa_translate.c)0
-rw-r--r--target/mips/tcg/mxu_translate.c (renamed from target/mips/mxu_translate.c)0
-rw-r--r--target/mips/tcg/op_helper.c420
-rw-r--r--target/mips/tcg/rel6_translate.c (renamed from target/mips/rel6_translate.c)0
-rw-r--r--target/mips/tcg/sysemu/cp0_helper.c (renamed from target/mips/cp0_helper.c)0
-rw-r--r--target/mips/tcg/sysemu/meson.build6
-rw-r--r--target/mips/tcg/sysemu/mips-semi.c (renamed from target/mips/mips-semi.c)0
-rw-r--r--target/mips/tcg/sysemu/special_helper.c173
-rw-r--r--target/mips/tcg/sysemu/tlb_helper.c (renamed from target/mips/tlb_helper.c)623
-rw-r--r--target/mips/tcg/sysemu_helper.h.inc185
-rw-r--r--target/mips/tcg/tcg-internal.h64
-rw-r--r--target/mips/tcg/translate.c (renamed from target/mips/translate.c)115
-rw-r--r--target/mips/tcg/translate_addr_const.c (renamed from target/mips/translate_addr_const.c)0
-rw-r--r--target/mips/tcg/tx79.decode (renamed from target/mips/tx79.decode)0
-rw-r--r--target/mips/tcg/tx79_translate.c (renamed from target/mips/tx79_translate.c)0
-rw-r--r--target/mips/tcg/txx9_translate.c (renamed from target/mips/txx9_translate.c)0
-rw-r--r--target/mips/tcg/user/meson.build3
-rw-r--r--target/mips/tcg/user/tlb_helper.c64
-rw-r--r--target/mips/translate.h7
-rw-r--r--tests/decode/succ_argset_type1.decode1
-rw-r--r--tests/qapi-schema/alternate-data-invalid.err2
-rw-r--r--tests/qapi-schema/alternate-data-invalid.json4
-rw-r--r--tests/qapi-schema/alternate-data-invalid.out0
-rw-r--r--tests/qapi-schema/meson.build2
-rw-r--r--tests/qapi-schema/union-invalid-data.err2
-rw-r--r--tests/qapi-schema/union-invalid-data.json6
-rw-r--r--tests/qapi-schema/union-invalid-data.out0
-rwxr-xr-xtests/qemu-iotests/2452
-rw-r--r--tests/qemu-iotests/283.out2
-rw-r--r--tests/qemu-iotests/tests/qsd-jobs.out2
-rw-r--r--tests/tcg/aarch64/Makefile.target2
-rw-r--r--tests/tcg/aarch64/mte-5.c44
-rw-r--r--tests/tcg/hexagon/Makefile.target6
-rw-r--r--tests/tcg/hexagon/brev.c190
-rw-r--r--tests/tcg/hexagon/circ.c486
-rw-r--r--tests/tcg/hexagon/fpstuff.c242
-rw-r--r--tests/tcg/hexagon/load_align.c415
-rw-r--r--tests/tcg/hexagon/load_unpack.c474
-rw-r--r--tests/tcg/hexagon/misc.c47
-rw-r--r--tests/tcg/hexagon/multi_result.c282
-rw-r--r--tests/unit/test-bdrv-drain.c2
-rw-r--r--tests/unit/test-bdrv-graph-mod.c209
-rw-r--r--util/meson.build1
-rw-r--r--util/transactions.c96
163 files changed, 8890 insertions, 4262 deletions
diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml
index 2d95784ed5..e44e4b49a2 100644
--- a/.gitlab-ci.d/crossbuilds.yml
+++ b/.gitlab-ci.d/crossbuilds.yml
@@ -176,6 +176,14 @@ cross-s390x-kvm-only:
IMAGE: debian-s390x-cross
ACCEL_CONFIGURE_OPTS: --disable-tcg
+cross-mips64el-kvm-only:
+ extends: .cross_accel_build_job
+ needs:
+ job: mips64el-debian-cross-container
+ variables:
+ IMAGE: debian-mips64el-cross
+ ACCEL_CONFIGURE_OPTS: --disable-tcg --target-list=mips64el-softmmu
+
cross-win32-system:
extends: .cross_system_build_job
needs:
diff --git a/MAINTAINERS b/MAINTAINERS
index 0d88146509..6c5b5693a8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -404,7 +404,8 @@ F: target/arm/kvm.c
MIPS KVM CPUs
M: Huacai Chen <chenhuacai@kernel.org>
S: Odd Fixes
-F: target/mips/kvm.c
+F: target/mips/kvm*
+F: target/mips/sysemu/
PPC KVM CPUs
M: David Gibson <david@gibson.dropbear.id.au>
@@ -2533,6 +2534,12 @@ M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
S: Maintained
F: scripts/simplebench/
+Transactions helper
+M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+S: Maintained
+F: include/qemu/transactions.h
+F: util/transactions.c
+
QAPI
M: Markus Armbruster <armbru@redhat.com>
M: Michael Roth <michael.roth@amd.com>
diff --git a/VERSION b/VERSION
index 09b254e90c..cc94f6b803 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-6.0.0
+6.0.50
diff --git a/block.c b/block.c
index c5b887cec1..874c22c43e 100644
--- a/block.c
+++ b/block.c
@@ -2,6 +2,7 @@
* QEMU System Emulator block driver
*
* Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2020 Virtuozzo International GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -82,6 +83,25 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
BdrvChildRole child_role,
Error **errp);
+static void bdrv_replace_child_noperm(BdrvChild *child,
+ BlockDriverState *new_bs);
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
+ BlockDriverState *child_bs,
+ const char *child_name,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ BdrvChild **child,
+ Transaction *tran,
+ Error **errp);
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
+ Transaction *tran);
+
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
+ BlockReopenQueue *queue,
+ Transaction *set_backings_tran, Error **errp);
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
+
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
@@ -1394,6 +1414,13 @@ static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
return 0;
}
+static AioContext *bdrv_child_cb_get_parent_aio_context(BdrvChild *c)
+{
+ BlockDriverState *bs = c->opaque;
+
+ return bdrv_get_aio_context(bs);
+}
+
const BdrvChildClass child_of_bds = {
.parent_is_bds = true,
.get_parent_desc = bdrv_child_get_parent_desc,
@@ -1407,8 +1434,14 @@ const BdrvChildClass child_of_bds = {
.can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
.set_aio_ctx = bdrv_child_cb_set_aio_ctx,
.update_filename = bdrv_child_cb_update_filename,
+ .get_parent_aio_context = bdrv_child_cb_get_parent_aio_context,
};
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c)
+{
+ return c->klass->get_parent_aio_context(c);
+}
+
static int bdrv_open_flags(BlockDriverState *bs, int flags)
{
int open_flags = flags;
@@ -1547,7 +1580,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
return ret;
}
- bdrv_refresh_limits(bs, &local_err);
+ bdrv_refresh_limits(bs, NULL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return -EINVAL;
@@ -1955,12 +1988,6 @@ static int bdrv_fill_options(QDict **options, const char *filename,
return 0;
}
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
- uint64_t perm, uint64_t shared,
- GSList *ignore_children, Error **errp);
-static void bdrv_child_abort_perm_update(BdrvChild *c);
-static void bdrv_child_set_perm(BdrvChild *c);
-
typedef struct BlockReopenQueueEntry {
bool prepared;
bool perms_checked;
@@ -2008,6 +2035,57 @@ bool bdrv_is_writable(BlockDriverState *bs)
return bdrv_is_writable_after_reopen(bs, NULL);
}
+static char *bdrv_child_user_desc(BdrvChild *c)
+{
+ if (c->klass->get_parent_desc) {
+ return c->klass->get_parent_desc(c);
+ }
+
+ return g_strdup("another user");
+}
+
+static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
+{
+ g_autofree char *user = NULL;
+ g_autofree char *perm_names = NULL;
+
+ if ((b->perm & a->shared_perm) == b->perm) {
+ return true;
+ }
+
+ perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
+ user = bdrv_child_user_desc(a);
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
+ "allow '%s' on %s",
+ user, a->name, perm_names, bdrv_get_node_name(b->bs));
+
+ return false;
+}
+
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
+{
+ BdrvChild *a, *b;
+
+ /*
+ * During the loop we'll look at each pair twice. That's correct because
+ * bdrv_a_allow_b() is asymmetric and we should check each pair in both
+ * directions.
+ */
+ QLIST_FOREACH(a, &bs->parents, next_parent) {
+ QLIST_FOREACH(b, &bs->parents, next_parent) {
+ if (a == b) {
+ continue;
+ }
+
+ if (!bdrv_a_allow_b(a, b, errp)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, BdrvChildRole role,
BlockReopenQueue *reopen_queue,
@@ -2025,22 +2103,186 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
}
/*
- * Check whether permissions on this node can be changed in a way that
- * @cumulative_perms and @cumulative_shared_perms are the new cumulative
- * permissions of all its parents. This involves checking whether all necessary
- * permission changes to child nodes can be performed.
+ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for
+ * nodes that are already in the @list, of course) so that final list is
+ * topologically sorted. Return the result (GSList @list object is updated, so
+ * don't use old reference after function call).
+ *
+ * On function start @list must be already topologically sorted and for any node
+ * in the @list the whole subtree of the node must be in the @list as well. The
+ * simplest way to satisfy this criteria: use only result of
+ * bdrv_topological_dfs() or NULL as @list parameter.
+ */
+static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
+ BlockDriverState *bs)
+{
+ BdrvChild *child;
+ g_autoptr(GHashTable) local_found = NULL;
+
+ if (!found) {
+ assert(!list);
+ found = local_found = g_hash_table_new(NULL, NULL);
+ }
+
+ if (g_hash_table_contains(found, bs)) {
+ return list;
+ }
+ g_hash_table_add(found, bs);
+
+ QLIST_FOREACH(child, &bs->children, next) {
+ list = bdrv_topological_dfs(list, found, child->bs);
+ }
+
+ return g_slist_prepend(list, bs);
+}
+
+typedef struct BdrvChildSetPermState {
+ BdrvChild *child;
+ uint64_t old_perm;
+ uint64_t old_shared_perm;
+} BdrvChildSetPermState;
+
+static void bdrv_child_set_perm_abort(void *opaque)
+{
+ BdrvChildSetPermState *s = opaque;
+
+ s->child->perm = s->old_perm;
+ s->child->shared_perm = s->old_shared_perm;
+}
+
+static TransactionActionDrv bdrv_child_set_pem_drv = {
+ .abort = bdrv_child_set_perm_abort,
+ .clean = g_free,
+};
+
+static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm,
+ uint64_t shared, Transaction *tran)
+{
+ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1);
+
+ *s = (BdrvChildSetPermState) {
+ .child = c,
+ .old_perm = c->perm,
+ .old_shared_perm = c->shared_perm,
+ };
+
+ c->perm = perm;
+ c->shared_perm = shared;
+
+ tran_add(tran, &bdrv_child_set_pem_drv, s);
+}
+
+static void bdrv_drv_set_perm_commit(void *opaque)
+{
+ BlockDriverState *bs = opaque;
+ uint64_t cumulative_perms, cumulative_shared_perms;
+
+ if (bs->drv->bdrv_set_perm) {
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
+ &cumulative_shared_perms);
+ bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
+ }
+}
+
+static void bdrv_drv_set_perm_abort(void *opaque)
+{
+ BlockDriverState *bs = opaque;
+
+ if (bs->drv->bdrv_abort_perm_update) {
+ bs->drv->bdrv_abort_perm_update(bs);
+ }
+}
+
+TransactionActionDrv bdrv_drv_set_perm_drv = {
+ .abort = bdrv_drv_set_perm_abort,
+ .commit = bdrv_drv_set_perm_commit,
+};
+
+static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
+ uint64_t shared_perm, Transaction *tran,
+ Error **errp)
+{
+ if (!bs->drv) {
+ return 0;
+ }
+
+ if (bs->drv->bdrv_check_perm) {
+ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (tran) {
+ tran_add(tran, &bdrv_drv_set_perm_drv, bs);
+ }
+
+ return 0;
+}
+
+typedef struct BdrvReplaceChildState {
+ BdrvChild *child;
+ BlockDriverState *old_bs;
+} BdrvReplaceChildState;
+
+static void bdrv_replace_child_commit(void *opaque)
+{
+ BdrvReplaceChildState *s = opaque;
+
+ bdrv_unref(s->old_bs);
+}
+
+static void bdrv_replace_child_abort(void *opaque)
+{
+ BdrvReplaceChildState *s = opaque;
+ BlockDriverState *new_bs = s->child->bs;
+
+ /* old_bs reference is transparently moved from @s to @s->child */
+ bdrv_replace_child_noperm(s->child, s->old_bs);
+ bdrv_unref(new_bs);
+}
+
+static TransactionActionDrv bdrv_replace_child_drv = {
+ .commit = bdrv_replace_child_commit,
+ .abort = bdrv_replace_child_abort,
+ .clean = g_free,
+};
+
+/*
+ * bdrv_replace_child
*
- * A call to this function must always be followed by a call to bdrv_set_perm()
- * or bdrv_abort_perm_update().
+ * Note: real unref of old_bs is done only on commit.
*/
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t cumulative_perms,
- uint64_t cumulative_shared_perms,
- GSList *ignore_children, Error **errp)
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
+ Transaction *tran)
+{
+ BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
+ *s = (BdrvReplaceChildState) {
+ .child = child,
+ .old_bs = child->bs,
+ };
+ tran_add(tran, &bdrv_replace_child_drv, s);
+
+ if (new_bs) {
+ bdrv_ref(new_bs);
+ }
+ bdrv_replace_child_noperm(child, new_bs);
+ /* old_bs reference is transparently moved from @child to @s */
+}
+
+/*
+ * Refresh permissions in @bs subtree. The function is intended to be called
+ * after some graph modification that was done without permission update.
+ */
+static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
+ Transaction *tran, Error **errp)
{
BlockDriver *drv = bs->drv;
BdrvChild *c;
int ret;
+ uint64_t cumulative_perms, cumulative_shared_perms;
+
+ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
@@ -2049,15 +2291,8 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
if (!bdrv_is_writable_after_reopen(bs, NULL)) {
error_setg(errp, "Block node is read-only");
} else {
- uint64_t current_perms, current_shared;
- bdrv_get_cumulative_perm(bs, &current_perms, &current_shared);
- if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
- error_setg(errp, "Cannot make block node read-only, there is "
- "a writer on it");
- } else {
- error_setg(errp, "Cannot make block node read-only and create "
- "a writer on it");
- }
+ error_setg(errp, "Read-only block node '%s' cannot support "
+ "read-write users", bdrv_get_node_name(bs));
}
return -EPERM;
@@ -2084,12 +2319,10 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
return 0;
}
- if (drv->bdrv_check_perm) {
- ret = drv->bdrv_check_perm(bs, cumulative_perms,
- cumulative_shared_perms, errp);
- if (ret < 0) {
- return ret;
- }
+ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran,
+ errp);
+ if (ret < 0) {
+ return ret;
}
/* Drivers that never have children can omit .bdrv_child_perm() */
@@ -2105,68 +2338,32 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
bdrv_child_perm(bs, c->bs, c, c->role, q,
cumulative_perms, cumulative_shared_perms,
&cur_perm, &cur_shared);
- ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children,
- errp);
- if (ret < 0) {
- return ret;
- }
+ bdrv_child_set_perm(c, cur_perm, cur_shared, tran);
}
return 0;
}
-/*
- * Notifies drivers that after a previous bdrv_check_perm() call, the
- * permission update is not performed and any preparations made for it (e.g.
- * taken file locks) need to be undone.
- *
- * This function recursively notifies all child nodes.
- */
-static void bdrv_abort_perm_update(BlockDriverState *bs)
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
+ Transaction *tran, Error **errp)
{
- BlockDriver *drv = bs->drv;
- BdrvChild *c;
-
- if (!drv) {
- return;
- }
-
- if (drv->bdrv_abort_perm_update) {
- drv->bdrv_abort_perm_update(bs);
- }
-
- QLIST_FOREACH(c, &bs->children, next) {
- bdrv_child_abort_perm_update(c);
- }
-}
-
-static void bdrv_set_perm(BlockDriverState *bs)
-{
- uint64_t cumulative_perms, cumulative_shared_perms;
- BlockDriver *drv = bs->drv;
- BdrvChild *c;
-
- if (!drv) {
- return;
- }
+ int ret;
+ BlockDriverState *bs;
- bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
+ for ( ; list; list = list->next) {
+ bs = list->data;
- /* Update this node */
- if (drv->bdrv_set_perm) {
- drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
- }
+ if (bdrv_parent_perms_conflict(bs, errp)) {
+ return -EINVAL;
+ }
- /* Drivers that never have children can omit .bdrv_child_perm() */
- if (!drv->bdrv_child_perm) {
- assert(QLIST_EMPTY(&bs->children));
- return;
+ ret = bdrv_node_refresh_perm(bs, q, tran, errp);
+ if (ret < 0) {
+ return ret;
+ }
}
- /* Update all children */
- QLIST_FOREACH(c, &bs->children, next) {
- bdrv_child_set_perm(c);
- }
+ return 0;
}
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
@@ -2185,15 +2382,6 @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
*shared_perm = cumulative_shared_perms;
}
-static char *bdrv_child_user_desc(BdrvChild *c)
-{
- if (c->klass->get_parent_desc) {
- return c->klass->get_parent_desc(c);
- }
-
- return g_strdup("another user");
-}
-
char *bdrv_perm_names(uint64_t perm)
{
struct perm_name {
@@ -2223,140 +2411,33 @@ char *bdrv_perm_names(uint64_t perm)
return g_string_free(result, FALSE);
}
-/*
- * Checks whether a new reference to @bs can be added if the new user requires
- * @new_used_perm/@new_shared_perm as its permissions. If @ignore_children is
- * set, the BdrvChild objects in this list are ignored in the calculations;
- * this allows checking permission updates for an existing reference.
- *
- * Needs to be followed by a call to either bdrv_set_perm() or
- * bdrv_abort_perm_update(). */
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t new_used_perm,
- uint64_t new_shared_perm,
- GSList *ignore_children,
- Error **errp)
-{
- BdrvChild *c;
- uint64_t cumulative_perms = new_used_perm;
- uint64_t cumulative_shared_perms = new_shared_perm;
-
-
- /* There is no reason why anyone couldn't tolerate write_unchanged */
- assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
-
- QLIST_FOREACH(c, &bs->parents, next_parent) {
- if (g_slist_find(ignore_children, c)) {
- continue;
- }
-
- if ((new_used_perm & c->shared_perm) != new_used_perm) {
- char *user = bdrv_child_user_desc(c);
- char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
-
- error_setg(errp, "Conflicts with use by %s as '%s', which does not "
- "allow '%s' on %s",
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
- g_free(user);
- g_free(perm_names);
- return -EPERM;
- }
-
- if ((c->perm & new_shared_perm) != c->perm) {
- char *user = bdrv_child_user_desc(c);
- char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
-
- error_setg(errp, "Conflicts with use by %s as '%s', which uses "
- "'%s' on %s",
- user, c->name, perm_names, bdrv_get_node_name(c->bs));
- g_free(user);
- g_free(perm_names);
- return -EPERM;
- }
-
- cumulative_perms |= c->perm;
- cumulative_shared_perms &= c->shared_perm;
- }
-
- return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
- ignore_children, errp);
-}
-
-/* Needs to be followed by a call to either bdrv_child_set_perm() or
- * bdrv_child_abort_perm_update(). */
-static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
- uint64_t perm, uint64_t shared,
- GSList *ignore_children, Error **errp)
-{
- int ret;
-
- ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
- ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
- g_slist_free(ignore_children);
-
- if (ret < 0) {
- return ret;
- }
-
- if (!c->has_backup_perm) {
- c->has_backup_perm = true;
- c->backup_perm = c->perm;
- c->backup_shared_perm = c->shared_perm;
- }
- /*
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
- * same child twice during check_perm procedure
- */
-
- c->perm = perm;
- c->shared_perm = shared;
-
- return 0;
-}
-
-static void bdrv_child_set_perm(BdrvChild *c)
-{
- c->has_backup_perm = false;
-
- bdrv_set_perm(c->bs);
-}
-
-static void bdrv_child_abort_perm_update(BdrvChild *c)
-{
- if (c->has_backup_perm) {
- c->perm = c->backup_perm;
- c->shared_perm = c->backup_shared_perm;
- c->has_backup_perm = false;
- }
-
- bdrv_abort_perm_update(c->bs);
-}
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
{
int ret;
- uint64_t perm, shared_perm;
+ Transaction *tran = tran_new();
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
- if (ret < 0) {
- bdrv_abort_perm_update(bs);
- return ret;
- }
- bdrv_set_perm(bs);
+ ret = bdrv_list_refresh_perms(list, NULL, tran, errp);
+ tran_finalize(tran, ret);
- return 0;
+ return ret;
}
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
Error **errp)
{
Error *local_err = NULL;
+ Transaction *tran = tran_new();
int ret;
- ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err);
+ bdrv_child_set_perm(c, perm, shared, tran);
+
+ ret = bdrv_refresh_perms(c->bs, &local_err);
+
+ tran_finalize(tran, ret);
+
if (ret < 0) {
- bdrv_child_abort_perm_update(c);
if ((perm & ~c->perm) || (c->shared_perm & ~shared)) {
/* tighten permissions */
error_propagate(errp, local_err);
@@ -2370,12 +2451,9 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
error_free(local_err);
ret = 0;
}
- return ret;
}
- bdrv_child_set_perm(c);
-
- return 0;
+ return ret;
}
int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp)
@@ -2627,37 +2705,177 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
}
}
+static void bdrv_child_free(void *opaque)
+{
+ BdrvChild *c = opaque;
+
+ g_free(c->name);
+ g_free(c);
+}
+
+static void bdrv_remove_empty_child(BdrvChild *child)
+{
+ assert(!child->bs);
+ QLIST_SAFE_REMOVE(child, next);
+ bdrv_child_free(child);
+}
+
+typedef struct BdrvAttachChildCommonState {
+ BdrvChild **child;
+ AioContext *old_parent_ctx;
+ AioContext *old_child_ctx;
+} BdrvAttachChildCommonState;
+
+static void bdrv_attach_child_common_abort(void *opaque)
+{
+ BdrvAttachChildCommonState *s = opaque;
+ BdrvChild *child = *s->child;
+ BlockDriverState *bs = child->bs;
+
+ bdrv_replace_child_noperm(child, NULL);
+
+ if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
+ bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
+ }
+
+ if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) {
+ GSList *ignore = g_slist_prepend(NULL, child);
+
+ child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore,
+ &error_abort);
+ g_slist_free(ignore);
+ ignore = g_slist_prepend(NULL, child);
+ child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
+
+ g_slist_free(ignore);
+ }
+
+ bdrv_unref(bs);
+ bdrv_remove_empty_child(child);
+ *s->child = NULL;
+}
+
+static TransactionActionDrv bdrv_attach_child_common_drv = {
+ .abort = bdrv_attach_child_common_abort,
+ .clean = g_free,
+};
+
/*
- * Updates @child to change its reference to point to @new_bs, including
- * checking and applying the necessary permission updates both to the old node
- * and to @new_bs.
- *
- * NULL is passed as @new_bs for removing the reference before freeing @child.
- *
- * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this
- * function uses bdrv_set_perm() to update the permissions according to the new
- * reference that @new_bs gets.
- *
- * Callers must ensure that child->frozen is false.
+ * Common part of attaching bdrv child to bs or to blk or to job
*/
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
+static int bdrv_attach_child_common(BlockDriverState *child_bs,
+ const char *child_name,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ uint64_t perm, uint64_t shared_perm,
+ void *opaque, BdrvChild **child,
+ Transaction *tran, Error **errp)
+{
+ BdrvChild *new_child;
+ AioContext *parent_ctx;
+ AioContext *child_ctx = bdrv_get_aio_context(child_bs);
+
+ assert(child);
+ assert(*child == NULL);
+
+ new_child = g_new(BdrvChild, 1);
+ *new_child = (BdrvChild) {
+ .bs = NULL,
+ .name = g_strdup(child_name),
+ .klass = child_class,
+ .role = child_role,
+ .perm = perm,
+ .shared_perm = shared_perm,
+ .opaque = opaque,
+ };
+
+ /*
+ * If the AioContexts don't match, first try to move the subtree of
+ * child_bs into the AioContext of the new parent. If this doesn't work,
+ * try moving the parent into the AioContext of child_bs instead.
+ */
+ parent_ctx = bdrv_child_get_parent_aio_context(new_child);
+ if (child_ctx != parent_ctx) {
+ Error *local_err = NULL;
+ int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err);
+
+ if (ret < 0 && child_class->can_set_aio_ctx) {
+ GSList *ignore = g_slist_prepend(NULL, new_child);
+ if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore,
+ NULL))
+ {
+ error_free(local_err);
+ ret = 0;
+ g_slist_free(ignore);
+ ignore = g_slist_prepend(NULL, new_child);
+ child_class->set_aio_ctx(new_child, child_ctx, &ignore);
+ }
+ g_slist_free(ignore);
+ }
+
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ bdrv_remove_empty_child(new_child);
+ return ret;
+ }
+ }
+
+ bdrv_ref(child_bs);
+ bdrv_replace_child_noperm(new_child, child_bs);
+
+ *child = new_child;
+
+ BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
+ *s = (BdrvAttachChildCommonState) {
+ .child = child,
+ .old_parent_ctx = parent_ctx,
+ .old_child_ctx = child_ctx,
+ };
+ tran_add(tran, &bdrv_attach_child_common_drv, s);
+
+ return 0;
+}
+
+static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
+ BlockDriverState *child_bs,
+ const char *child_name,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ BdrvChild **child,
+ Transaction *tran,
+ Error **errp)
{
- BlockDriverState *old_bs = child->bs;
+ int ret;
+ uint64_t perm, shared_perm;
- /* Asserts that child->frozen == false */
- bdrv_replace_child_noperm(child, new_bs);
+ assert(parent_bs->drv);
+ bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
+ perm, shared_perm, &perm, &shared_perm);
+
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
+ child_role, perm, shared_perm, parent_bs,
+ child, tran, errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ QLIST_INSERT_HEAD(&parent_bs->children, *child, next);
/*
- * Start with the new node's permissions. If @new_bs is a (direct
- * or indirect) child of @old_bs, we must complete the permission
- * update on @new_bs before we loosen the restrictions on @old_bs.
- * Otherwise, bdrv_check_perm() on @old_bs would re-initiate
- * updating the permissions of @new_bs, and thus not purely loosen
- * restrictions.
+ * child is removed in bdrv_attach_child_common_abort(), so don't care to
+ * abort this change separately.
*/
- if (new_bs) {
- bdrv_set_perm(new_bs);
- }
+
+ return 0;
+}
+
+static void bdrv_detach_child(BdrvChild *child)
+{
+ BlockDriverState *old_bs = child->bs;
+
+ bdrv_replace_child_noperm(child, NULL);
+ bdrv_remove_empty_child(child);
if (old_bs) {
/*
@@ -2667,8 +2885,10 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
*/
bdrv_refresh_perms(old_bs, NULL);
- /* When the parent requiring a non-default AioContext is removed, the
- * node moves back to the main AioContext */
+ /*
+ * When the parent requiring a non-default AioContext is removed, the
+ * node moves back to the main AioContext
+ */
bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
}
}
@@ -2687,61 +2907,25 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
const BdrvChildClass *child_class,
BdrvChildRole child_role,
- AioContext *ctx,
uint64_t perm, uint64_t shared_perm,
void *opaque, Error **errp)
{
- BdrvChild *child;
- Error *local_err = NULL;
int ret;
+ BdrvChild *child = NULL;
+ Transaction *tran = tran_new();
- ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
+ ret = bdrv_attach_child_common(child_bs, child_name, child_class,
+ child_role, perm, shared_perm, opaque,
+ &child, tran, errp);
if (ret < 0) {
- bdrv_abort_perm_update(child_bs);
bdrv_unref(child_bs);
return NULL;
}
- child = g_new(BdrvChild, 1);
- *child = (BdrvChild) {
- .bs = NULL,
- .name = g_strdup(child_name),
- .klass = child_class,
- .role = child_role,
- .perm = perm,
- .shared_perm = shared_perm,
- .opaque = opaque,
- };
-
- /* If the AioContexts don't match, first try to move the subtree of
- * child_bs into the AioContext of the new parent. If this doesn't work,
- * try moving the parent into the AioContext of child_bs instead. */
- if (bdrv_get_aio_context(child_bs) != ctx) {
- ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
- if (ret < 0 && child_class->can_set_aio_ctx) {
- GSList *ignore = g_slist_prepend(NULL, child);
- ctx = bdrv_get_aio_context(child_bs);
- if (child_class->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
- error_free(local_err);
- ret = 0;
- g_slist_free(ignore);
- ignore = g_slist_prepend(NULL, child);
- child_class->set_aio_ctx(child, ctx, &ignore);
- }
- g_slist_free(ignore);
- }
- if (ret < 0) {
- error_propagate(errp, local_err);
- g_free(child);
- bdrv_abort_perm_update(child_bs);
- bdrv_unref(child_bs);
- return NULL;
- }
- }
-
- /* This performs the matching bdrv_set_perm() for the above check. */
- bdrv_replace_child(child, child_bs);
+ ret = bdrv_refresh_perms(child_bs, errp);
+ tran_finalize(tran, ret);
+ bdrv_unref(child_bs);
return child;
}
@@ -2763,34 +2947,27 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
BdrvChildRole child_role,
Error **errp)
{
- BdrvChild *child;
- uint64_t perm, shared_perm;
-
- bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
-
- assert(parent_bs->drv);
- bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
- perm, shared_perm, &perm, &shared_perm);
+ int ret;
+ BdrvChild *child = NULL;
+ Transaction *tran = tran_new();
- child = bdrv_root_attach_child(child_bs, child_name, child_class,
- child_role, bdrv_get_aio_context(parent_bs),
- perm, shared_perm, parent_bs, errp);
- if (child == NULL) {
- return NULL;
+ ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class,
+ child_role, &child, tran, errp);
+ if (ret < 0) {
+ goto out;
}
- QLIST_INSERT_HEAD(&parent_bs->children, child, next);
- return child;
-}
+ ret = bdrv_refresh_perms(parent_bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
-static void bdrv_detach_child(BdrvChild *child)
-{
- QLIST_SAFE_REMOVE(child, next);
+out:
+ tran_finalize(tran, ret);
- bdrv_replace_child(child, NULL);
+ bdrv_unref(child_bs);
- g_free(child->name);
- g_free(child);
+ return child;
}
/* Callers must ensure that child->frozen is false. */
@@ -2803,11 +2980,49 @@ void bdrv_root_unref_child(BdrvChild *child)
bdrv_unref(child_bs);
}
+typedef struct BdrvSetInheritsFrom {
+ BlockDriverState *bs;
+ BlockDriverState *old_inherits_from;
+} BdrvSetInheritsFrom;
+
+static void bdrv_set_inherits_from_abort(void *opaque)
+{
+ BdrvSetInheritsFrom *s = opaque;
+
+ s->bs->inherits_from = s->old_inherits_from;
+}
+
+static TransactionActionDrv bdrv_set_inherits_from_drv = {
+ .abort = bdrv_set_inherits_from_abort,
+ .clean = g_free,
+};
+
+/* @tran is allowed to be NULL. In this case no rollback is possible */
+static void bdrv_set_inherits_from(BlockDriverState *bs,
+ BlockDriverState *new_inherits_from,
+ Transaction *tran)
+{
+ if (tran) {
+ BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1);
+
+ *s = (BdrvSetInheritsFrom) {
+ .bs = bs,
+ .old_inherits_from = bs->inherits_from,
+ };
+
+ tran_add(tran, &bdrv_set_inherits_from_drv, s);
+ }
+
+ bs->inherits_from = new_inherits_from;
+}
+
/**
* Clear all inherits_from pointers from children and grandchildren of
* @root that point to @root, where necessary.
+ * @tran is allowed to be NULL. In this case no rollback is possible
*/
-static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
+static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child,
+ Transaction *tran)
{
BdrvChild *c;
@@ -2822,12 +3037,12 @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child)
}
}
if (c == NULL) {
- child->bs->inherits_from = NULL;
+ bdrv_set_inherits_from(child->bs, NULL, tran);
}
}
QLIST_FOREACH(c, &child->bs->children, next) {
- bdrv_unset_inherits_from(root, c);
+ bdrv_unset_inherits_from(root, c, tran);
}
}
@@ -2838,7 +3053,7 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
return;
}
- bdrv_unset_inherits_from(parent, child);
+ bdrv_unset_inherits_from(parent, child, NULL);
bdrv_root_unref_child(child);
}
@@ -2883,8 +3098,9 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
* Sets the bs->backing link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
*/
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
- Error **errp)
+static int bdrv_set_backing_noperm(BlockDriverState *bs,
+ BlockDriverState *backing_hd,
+ Transaction *tran, Error **errp)
{
int ret = 0;
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
@@ -2894,36 +3110,53 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
return -EPERM;
}
- if (backing_hd) {
- bdrv_ref(backing_hd);
- }
-
if (bs->backing) {
/* Cannot be frozen, we checked that above */
- bdrv_unref_child(bs, bs->backing);
- bs->backing = NULL;
+ bdrv_unset_inherits_from(bs, bs->backing, tran);
+ bdrv_remove_filter_or_cow_child(bs, tran);
}
if (!backing_hd) {
goto out;
}
- bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds,
- bdrv_backing_role(bs), errp);
- if (!bs->backing) {
- ret = -EPERM;
- goto out;
+ ret = bdrv_attach_child_noperm(bs, backing_hd, "backing",
+ &child_of_bds, bdrv_backing_role(bs),
+ &bs->backing, tran, errp);
+ if (ret < 0) {
+ return ret;
}
- /* If backing_hd was already part of bs's backing chain, and
+
+ /*
+ * If backing_hd was already part of bs's backing chain, and
* inherits_from pointed recursively to bs then let's update it to
- * point directly to bs (else it will become NULL). */
+ * point directly to bs (else it will become NULL).
+ */
if (update_inherits_from) {
- backing_hd->inherits_from = bs;
+ bdrv_set_inherits_from(backing_hd, bs, tran);
+ }
+
+out:
+ bdrv_refresh_limits(bs, tran, NULL);
+
+ return 0;
+}
+
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
+ Error **errp)
+{
+ int ret;
+ Transaction *tran = tran_new();
+
+ ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
+ if (ret < 0) {
+ goto out;
}
+ ret = bdrv_refresh_perms(bs, errp);
out:
- bdrv_refresh_limits(bs, NULL);
+ tran_finalize(tran, ret);
return ret;
}
@@ -3213,11 +3446,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
goto out;
}
- /* bdrv_append() consumes a strong reference to bs_snapshot
- * (i.e. it will call bdrv_unref() on it) even on error, so in
- * order to be able to return one, we have to increase
- * bs_snapshot's refcount here */
- bdrv_ref(bs_snapshot);
ret = bdrv_append(bs_snapshot, bs, errp);
if (ret < 0) {
bs_snapshot = NULL;
@@ -3729,10 +3957,6 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
bs_entry->state.explicit_options = explicit_options;
bs_entry->state.flags = flags;
- /* This needs to be overwritten in bdrv_reopen_prepare() */
- bs_entry->state.perm = UINT64_MAX;
- bs_entry->state.shared_perm = 0;
-
/*
* If keep_old_opts is false then it means that unspecified
* options must be reset to their original value. We don't allow
@@ -3817,38 +4041,49 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
{
int ret = -1;
BlockReopenQueueEntry *bs_entry, *next;
+ Transaction *tran = tran_new();
+ g_autoptr(GHashTable) found = NULL;
+ g_autoptr(GSList) refresh_list = NULL;
assert(bs_queue != NULL);
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
- assert(bs_entry->state.bs->quiesce_counter > 0);
- if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) {
+ ret = bdrv_flush(bs_entry->state.bs);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Error flushing drive");
goto cleanup;
}
+ }
+
+ QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
+ assert(bs_entry->state.bs->quiesce_counter > 0);
+ ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp);
+ if (ret < 0) {
+ goto abort;
+ }
bs_entry->prepared = true;
}
+ found = g_hash_table_new(NULL, NULL);
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
BDRVReopenState *state = &bs_entry->state;
- ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
- state->shared_perm, NULL, errp);
- if (ret < 0) {
- goto cleanup_perm;
- }
- /* Check if new_backing_bs would accept the new permissions */
- if (state->replace_backing_bs && state->new_backing_bs) {
- uint64_t nperm, nshared;
- bdrv_child_perm(state->bs, state->new_backing_bs,
- NULL, bdrv_backing_role(state->bs),
- bs_queue, state->perm, state->shared_perm,
- &nperm, &nshared);
- ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
- nperm, nshared, NULL, errp);
- if (ret < 0) {
- goto cleanup_perm;
- }
+
+ refresh_list = bdrv_topological_dfs(refresh_list, found, state->bs);
+ if (state->old_backing_bs) {
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
+ state->old_backing_bs);
}
- bs_entry->perms_checked = true;
+ }
+
+ /*
+ * Note that file-posix driver rely on permission update done during reopen
+ * (even if no permission changed), because it wants "new" permissions for
+ * reconfiguring the fd and that's why it does it in raw_check_perm(), not
+ * in raw_reopen_prepare() which is called with "old" permissions.
+ */
+ ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp);
+ if (ret < 0) {
+ goto abort;
}
/*
@@ -3864,51 +4099,31 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
bdrv_reopen_commit(&bs_entry->state);
}
- ret = 0;
-cleanup_perm:
- QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
- BDRVReopenState *state = &bs_entry->state;
-
- if (!bs_entry->perms_checked) {
- continue;
- }
-
- if (ret == 0) {
- uint64_t perm, shared;
+ tran_commit(tran);
- bdrv_get_cumulative_perm(state->bs, &perm, &shared);
- assert(perm == state->perm);
- assert(shared == state->shared_perm);
+ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
+ BlockDriverState *bs = bs_entry->state.bs;
- bdrv_set_perm(state->bs);
- } else {
- bdrv_abort_perm_update(state->bs);
- if (state->replace_backing_bs && state->new_backing_bs) {
- bdrv_abort_perm_update(state->new_backing_bs);
- }
+ if (bs->drv->bdrv_reopen_commit_post) {
+ bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
}
}
- if (ret == 0) {
- QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) {
- BlockDriverState *bs = bs_entry->state.bs;
+ ret = 0;
+ goto cleanup;
- if (bs->drv->bdrv_reopen_commit_post)
- bs->drv->bdrv_reopen_commit_post(&bs_entry->state);
+abort:
+ tran_abort(tran);
+ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
+ if (bs_entry->prepared) {
+ bdrv_reopen_abort(&bs_entry->state);
}
+ qobject_unref(bs_entry->state.explicit_options);
+ qobject_unref(bs_entry->state.options);
}
+
cleanup:
QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
- if (ret) {
- if (bs_entry->prepared) {
- bdrv_reopen_abort(&bs_entry->state);
- }
- qobject_unref(bs_entry->state.explicit_options);
- qobject_unref(bs_entry->state.options);
- }
- if (bs_entry->state.new_backing_bs) {
- bdrv_unref(bs_entry->state.new_backing_bs);
- }
g_free(bs_entry);
}
g_free(bs_queue);
@@ -3933,53 +4148,6 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
return ret;
}
-static BlockReopenQueueEntry *find_parent_in_reopen_queue(BlockReopenQueue *q,
- BdrvChild *c)
-{
- BlockReopenQueueEntry *entry;
-
- QTAILQ_FOREACH(entry, q, entry) {
- BlockDriverState *bs = entry->state.bs;
- BdrvChild *child;
-
- QLIST_FOREACH(child, &bs->children, next) {
- if (child == c) {
- return entry;
- }
- }
- }
-
- return NULL;
-}
-
-static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
- uint64_t *perm, uint64_t *shared)
-{
- BdrvChild *c;
- BlockReopenQueueEntry *parent;
- uint64_t cumulative_perms = 0;
- uint64_t cumulative_shared_perms = BLK_PERM_ALL;
-
- QLIST_FOREACH(c, &bs->parents, next_parent) {
- parent = find_parent_in_reopen_queue(q, c);
- if (!parent) {
- cumulative_perms |= c->perm;
- cumulative_shared_perms &= c->shared_perm;
- } else {
- uint64_t nperm, nshared;
-
- bdrv_child_perm(parent->state.bs, bs, c, c->role, q,
- parent->state.perm, parent->state.shared_perm,
- &nperm, &nshared);
-
- cumulative_perms |= nperm;
- cumulative_shared_perms &= nshared;
- }
- }
- *perm = cumulative_perms;
- *shared = cumulative_shared_perms;
-}
-
static bool bdrv_reopen_can_attach(BlockDriverState *parent,
BdrvChild *child,
BlockDriverState *new_child,
@@ -4021,6 +4189,7 @@ static bool bdrv_reopen_can_attach(BlockDriverState *parent,
* Return 0 on success, otherwise return < 0 and set @errp.
*/
static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
+ Transaction *set_backings_tran,
Error **errp)
{
BlockDriverState *bs = reopen_state->bs;
@@ -4097,6 +4266,8 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
/* If we want to replace the backing file we need some extra checks */
if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
+ int ret;
+
/* Check for implicit nodes between bs and its backing file */
if (bs != overlay_bs) {
error_setg(errp, "Cannot change backing link if '%s' has "
@@ -4117,9 +4288,11 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
return -EPERM;
}
reopen_state->replace_backing_bs = true;
- if (new_backing_bs) {
- bdrv_ref(new_backing_bs);
- reopen_state->new_backing_bs = new_backing_bs;
+ reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
+ ret = bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
+ errp);
+ if (ret < 0) {
+ return ret;
}
}
@@ -4143,8 +4316,9 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
* commit() for any other BDS that have been left in a prepare() state
*
*/
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
- Error **errp)
+static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
+ BlockReopenQueue *queue,
+ Transaction *set_backings_tran, Error **errp)
{
int ret = -1;
int old_flags;
@@ -4211,16 +4385,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
goto error;
}
- /* Calculate required permissions after reopening */
- bdrv_reopen_perm(queue, reopen_state->bs,
- &reopen_state->perm, &reopen_state->shared_perm);
-
- ret = bdrv_flush(reopen_state->bs);
- if (ret) {
- error_setg_errno(errp, -ret, "Error flushing drive");
- goto error;
- }
-
if (drv->bdrv_reopen_prepare) {
/*
* If a driver-specific option is missing, it means that we
@@ -4274,7 +4438,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
* either a reference to an existing node (using its node name)
* or NULL to simply detach the current backing file.
*/
- ret = bdrv_reopen_parse_backing(reopen_state, errp);
+ ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
if (ret < 0) {
goto error;
}
@@ -4359,7 +4523,7 @@ error:
* makes them final by swapping the staging BlockDriverState contents into
* the active BlockDriverState contents.
*/
-void bdrv_reopen_commit(BDRVReopenState *reopen_state)
+static void bdrv_reopen_commit(BDRVReopenState *reopen_state)
{
BlockDriver *drv;
BlockDriverState *bs;
@@ -4396,30 +4560,14 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
qdict_del(bs->explicit_options, child->name);
qdict_del(bs->options, child->name);
}
-
- /*
- * Change the backing file if a new one was specified. We do this
- * after updating bs->options, so bdrv_refresh_filename() (called
- * from bdrv_set_backing_hd()) has the new values.
- */
- if (reopen_state->replace_backing_bs) {
- BlockDriverState *old_backing_bs = child_bs(bs->backing);
- assert(!old_backing_bs || !old_backing_bs->implicit);
- /* Abort the permission update on the backing bs we're detaching */
- if (old_backing_bs) {
- bdrv_abort_perm_update(old_backing_bs);
- }
- bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort);
- }
-
- bdrv_refresh_limits(bs, NULL);
+ bdrv_refresh_limits(bs, NULL, NULL);
}
/*
* Abort the reopen, and delete and free the staged changes in
* reopen_state
*/
-void bdrv_reopen_abort(BDRVReopenState *reopen_state)
+static void bdrv_reopen_abort(BDRVReopenState *reopen_state)
{
BlockDriver *drv;
@@ -4585,78 +4733,176 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
return ret;
}
+typedef struct BdrvRemoveFilterOrCowChild {
+ BdrvChild *child;
+ bool is_backing;
+} BdrvRemoveFilterOrCowChild;
+
+static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
+{
+ BdrvRemoveFilterOrCowChild *s = opaque;
+ BlockDriverState *parent_bs = s->child->opaque;
+
+ QLIST_INSERT_HEAD(&parent_bs->children, s->child, next);
+ if (s->is_backing) {
+ parent_bs->backing = s->child;
+ } else {
+ parent_bs->file = s->child;
+ }
+
+ /*
+ * We don't have to restore child->bs here to undo bdrv_replace_child()
+ * because that function is transactionable and it registered own completion
+ * entries in @tran, so .abort() for bdrv_replace_child_safe() will be
+ * called automatically.
+ */
+}
+
+static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
+{
+ BdrvRemoveFilterOrCowChild *s = opaque;
+
+ bdrv_child_free(s->child);
+}
+
+static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
+ .abort = bdrv_remove_filter_or_cow_child_abort,
+ .commit = bdrv_remove_filter_or_cow_child_commit,
+ .clean = g_free,
+};
+
/*
- * With auto_skip=true bdrv_replace_node_common skips updating from parents
- * if it creates a parent-child relation loop or if parent is block-job.
- *
- * With auto_skip=false the error is returned if from has a parent which should
- * not be updated.
+ * A function to remove backing-chain child of @bs if exists: cow child for
+ * format nodes (always .backing) and filter child for filters (may be .file or
+ * .backing)
*/
-static int bdrv_replace_node_common(BlockDriverState *from,
- BlockDriverState *to,
- bool auto_skip, Error **errp)
+static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
+ Transaction *tran)
{
- BdrvChild *c, *next;
- GSList *list = NULL, *p;
- uint64_t perm = 0, shared = BLK_PERM_ALL;
- int ret;
+ BdrvRemoveFilterOrCowChild *s;
+ BdrvChild *child = bdrv_filter_or_cow_child(bs);
- /* Make sure that @from doesn't go away until we have successfully attached
- * all of its parents to @to. */
- bdrv_ref(from);
+ if (!child) {
+ return;
+ }
- assert(qemu_get_current_aio_context() == qemu_get_aio_context());
- assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
- bdrv_drained_begin(from);
+ if (child->bs) {
+ bdrv_replace_child(child, NULL, tran);
+ }
+
+ s = g_new(BdrvRemoveFilterOrCowChild, 1);
+ *s = (BdrvRemoveFilterOrCowChild) {
+ .child = child,
+ .is_backing = (child == bs->backing),
+ };
+ tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
+
+ QLIST_SAFE_REMOVE(child, next);
+ if (s->is_backing) {
+ bs->backing = NULL;
+ } else {
+ bs->file = NULL;
+ }
+}
+
+static int bdrv_replace_node_noperm(BlockDriverState *from,
+ BlockDriverState *to,
+ bool auto_skip, Transaction *tran,
+ Error **errp)
+{
+ BdrvChild *c, *next;
- /* Put all parents into @list and calculate their cumulative permissions */
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
assert(c->bs == from);
if (!should_update_child(c, to)) {
if (auto_skip) {
continue;
}
- ret = -EINVAL;
error_setg(errp, "Should not change '%s' link to '%s'",
c->name, from->node_name);
- goto out;
+ return -EINVAL;
}
if (c->frozen) {
- ret = -EPERM;
error_setg(errp, "Cannot change '%s' link to '%s'",
c->name, from->node_name);
- goto out;
+ return -EPERM;
}
- list = g_slist_prepend(list, c);
- perm |= c->perm;
- shared &= c->shared_perm;
+ bdrv_replace_child(c, to, tran);
}
- /* Check whether the required permissions can be granted on @to, ignoring
- * all BdrvChild in @list so that they can't block themselves. */
- ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp);
+ return 0;
+}
+
+/*
+ * With auto_skip=true bdrv_replace_node_common skips updating from parents
+ * if it creates a parent-child relation loop or if parent is block-job.
+ *
+ * With auto_skip=false the error is returned if from has a parent which should
+ * not be updated.
+ *
+ * With @detach_subchain=true @to must be in a backing chain of @from. In this
+ * case backing link of the cow-parent of @to is removed.
+ */
+static int bdrv_replace_node_common(BlockDriverState *from,
+ BlockDriverState *to,
+ bool auto_skip, bool detach_subchain,
+ Error **errp)
+{
+ Transaction *tran = tran_new();
+ g_autoptr(GHashTable) found = NULL;
+ g_autoptr(GSList) refresh_list = NULL;
+ BlockDriverState *to_cow_parent;
+ int ret;
+
+ if (detach_subchain) {
+ assert(bdrv_chain_contains(from, to));
+ assert(from != to);
+ for (to_cow_parent = from;
+ bdrv_filter_or_cow_bs(to_cow_parent) != to;
+ to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent))
+ {
+ ;
+ }
+ }
+
+ /* Make sure that @from doesn't go away until we have successfully attached
+ * all of its parents to @to. */
+ bdrv_ref(from);
+
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+ assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
+ bdrv_drained_begin(from);
+
+ /*
+ * Do the replacement without permission update.
+ * Replacement may influence the permissions, we should calculate new
+ * permissions based on new graph. If we fail, we'll roll-back the
+ * replacement.
+ */
+ ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp);
if (ret < 0) {
- bdrv_abort_perm_update(to);
goto out;
}
- /* Now actually perform the change. We performed the permission check for
- * all elements of @list at once, so set the permissions all at once at the
- * very end. */
- for (p = list; p != NULL; p = p->next) {
- c = p->data;
-
- bdrv_ref(to);
- bdrv_replace_child_noperm(c, to);
- bdrv_unref(from);
+ if (detach_subchain) {
+ bdrv_remove_filter_or_cow_child(to_cow_parent, tran);
}
- bdrv_set_perm(to);
+ found = g_hash_table_new(NULL, NULL);
+
+ refresh_list = bdrv_topological_dfs(refresh_list, found, to);
+ refresh_list = bdrv_topological_dfs(refresh_list, found, from);
+
+ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
+ if (ret < 0) {
+ goto out;
+ }
ret = 0;
out:
- g_slist_free(list);
+ tran_finalize(tran, ret);
+
bdrv_drained_end(from);
bdrv_unref(from);
@@ -4666,7 +4912,13 @@ out:
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
- return bdrv_replace_node_common(from, to, true, errp);
+ return bdrv_replace_node_common(from, to, true, false, errp);
+}
+
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
+{
+ return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true,
+ errp);
}
/*
@@ -4676,37 +4928,36 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
* This will modify the BlockDriverState fields, and swap contents
* between bs_new and bs_top. Both bs_new and bs_top are modified.
*
- * bs_new must not be attached to a BlockBackend.
+ * bs_new must not be attached to a BlockBackend and must not have backing
+ * child.
*
* This function does not create any image files.
- *
- * bdrv_append() takes ownership of a bs_new reference and unrefs it because
- * that's what the callers commonly need. bs_new will be referenced by the old
- * parents of bs_top after bdrv_append() returns. If the caller needs to keep a
- * reference of its own, it must call bdrv_ref().
*/
int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
Error **errp)
{
- int ret = bdrv_set_backing_hd(bs_new, bs_top, errp);
+ int ret;
+ Transaction *tran = tran_new();
+
+ assert(!bs_new->backing);
+
+ ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
+ &child_of_bds, bdrv_backing_role(bs_new),
+ &bs_new->backing, tran, errp);
if (ret < 0) {
goto out;
}
- ret = bdrv_replace_node(bs_top, bs_new, errp);
+ ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp);
if (ret < 0) {
- bdrv_set_backing_hd(bs_new, NULL, &error_abort);
goto out;
}
- ret = 0;
-
+ ret = bdrv_refresh_perms(bs_new, errp);
out:
- /*
- * bs_new is now referenced by its new parents, we don't need the
- * additional reference any more.
- */
- bdrv_unref(bs_new);
+ tran_finalize(tran, ret);
+
+ bdrv_refresh_limits(bs_top, NULL, NULL);
return ret;
}
@@ -5002,7 +5253,17 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
updated_children = g_slist_prepend(updated_children, c);
}
- bdrv_replace_node_common(top, base, false, &local_err);
+ /*
+ * It seems correct to pass detach_subchain=true here, but it triggers
+ * one more yet not fixed bug, when due to nested aio_poll loop we switch to
+ * another drained section, which modify the graph (for example, removing
+ * the child, which we keep in updated_children list). So, it's a TODO.
+ *
+ * Note, bug triggered if pass detach_subchain=true here and run
+ * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash.
+ * That's a FIXME.
+ */
+ bdrv_replace_node_common(top, base, false, false, &local_err);
if (local_err) {
error_report_err(local_err);
goto exit;
diff --git a/block/backup-top.c b/block/backup-top.c
index 589e8b651d..425e3778be 100644
--- a/block/backup-top.c
+++ b/block/backup-top.c
@@ -37,7 +37,6 @@
typedef struct BDRVBackupTopState {
BlockCopyState *bcs;
BdrvChild *target;
- bool active;
int64_t cluster_size;
} BDRVBackupTopState;
@@ -45,12 +44,6 @@ static coroutine_fn int backup_top_co_preadv(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
- BDRVBackupTopState *s = bs->opaque;
-
- if (!s->active) {
- return -EIO;
- }
-
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
@@ -60,10 +53,6 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
BDRVBackupTopState *s = bs->opaque;
uint64_t off, end;
- if (!s->active) {
- return -EIO;
- }
-
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
return 0;
}
@@ -137,21 +126,6 @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
- BDRVBackupTopState *s = bs->opaque;
-
- if (!s->active) {
- /*
- * The filter node may be in process of bdrv_append(), which firstly do
- * bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
- * we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
- * let's require nothing during bdrv_append() and refresh permissions
- * after it (see bdrv_backup_top_append()).
- */
- *nperm = 0;
- *nshared = BLK_PERM_ALL;
- return;
- }
-
if (!(role & BDRV_CHILD_FILTERED)) {
/*
* Target child
@@ -234,7 +208,6 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
bdrv_drained_begin(source);
- bdrv_ref(top);
ret = bdrv_append(top, source, errp);
if (ret < 0) {
error_prepend(errp, "Cannot append backup-top filter: ");
@@ -242,17 +215,6 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
}
appended = true;
- /*
- * bdrv_append() finished successfully, now we can require permissions
- * we want.
- */
- state->active = true;
- ret = bdrv_child_refresh_perms(top, top->backing, errp);
- if (ret < 0) {
- error_prepend(errp, "Cannot set permissions for backup-top filter: ");
- goto fail;
- }
-
state->cluster_size = cluster_size;
state->bcs = block_copy_state_new(top->backing, state->target,
cluster_size, perf->use_copy_range,
@@ -269,7 +231,6 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
fail:
if (appended) {
- state->active = false;
bdrv_backup_top_drop(top);
} else {
bdrv_unref(top);
@@ -284,16 +245,9 @@ void bdrv_backup_top_drop(BlockDriverState *bs)
{
BDRVBackupTopState *s = bs->opaque;
- bdrv_drained_begin(bs);
+ bdrv_drop_filter(bs, &error_abort);
block_copy_state_free(s->bcs);
- s->active = false;
- bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
- bdrv_replace_node(bs, bs->backing->bs, &error_abort);
- bdrv_set_backing_hd(bs, NULL, &error_abort);
-
- bdrv_drained_end(bs);
-
bdrv_unref(bs);
}
diff --git a/block/block-backend.c b/block/block-backend.c
index 413af51f3b..6fca9853e1 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -298,6 +298,13 @@ static void blk_root_detach(BdrvChild *child)
}
}
+static AioContext *blk_root_get_parent_aio_context(BdrvChild *c)
+{
+ BlockBackend *blk = c->opaque;
+
+ return blk_get_aio_context(blk);
+}
+
static const BdrvChildClass child_root = {
.inherit_options = blk_root_inherit_options,
@@ -318,6 +325,8 @@ static const BdrvChildClass child_root = {
.can_set_aio_ctx = blk_root_can_set_aio_ctx,
.set_aio_ctx = blk_root_set_aio_ctx,
+
+ .get_parent_aio_context = blk_root_get_parent_aio_context,
};
/*
@@ -398,15 +407,19 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
BlockBackend *blk;
BlockDriverState *bs;
uint64_t perm = 0;
+ uint64_t shared = BLK_PERM_ALL;
- /* blk_new_open() is mainly used in .bdrv_create implementations and the
- * tools where sharing isn't a concern because the BDS stays private, so we
- * just request permission according to the flags.
+ /*
+ * blk_new_open() is mainly used in .bdrv_create implementations and the
+ * tools where sharing isn't a major concern because the BDS stays private
+ * and the file is generally not supposed to be used by a second process,
+ * so we just request permission according to the flags.
*
* The exceptions are xen_disk and blockdev_init(); in these cases, the
* caller of blk_new_open() doesn't make use of the permissions, but they
* shouldn't hurt either. We can still share everything here because the
- * guest devices will add their own blockers if they can't share. */
+ * guest devices will add their own blockers if they can't share.
+ */
if ((flags & BDRV_O_NO_IO) == 0) {
perm |= BLK_PERM_CONSISTENT_READ;
if (flags & BDRV_O_RDWR) {
@@ -416,8 +429,11 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
if (flags & BDRV_O_RESIZE) {
perm |= BLK_PERM_RESIZE;
}
+ if (flags & BDRV_O_NO_SHARE) {
+ shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
+ }
- blk = blk_new(qemu_get_aio_context(), perm, BLK_PERM_ALL);
+ blk = blk_new(qemu_get_aio_context(), perm, shared);
bs = bdrv_open(filename, reference, options, flags, errp);
if (!bs) {
blk_unref(blk);
@@ -426,7 +442,7 @@ BlockBackend *blk_new_open(const char *filename, const char *reference,
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- blk->ctx, perm, BLK_PERM_ALL, blk, errp);
+ perm, shared, blk, errp);
if (!blk->root) {
blk_unref(blk);
return NULL;
@@ -840,7 +856,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
bdrv_ref(bs);
blk->root = bdrv_root_attach_child(bs, "root", &child_root,
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- blk->ctx, blk->perm, blk->shared_perm,
+ blk->perm, blk->shared_perm,
blk, errp);
if (blk->root == NULL) {
return -EPERM;
diff --git a/block/commit.c b/block/commit.c
index dd9ba87349..b89bb20b75 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -312,6 +312,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
commit_top_bs->total_sectors = top->total_sectors;
ret = bdrv_append(commit_top_bs, top, errp);
+ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */
if (ret < 0) {
commit_top_bs = NULL;
goto fail;
diff --git a/block/file-posix.c b/block/file-posix.c
index 20e14f8e96..10b71d9a13 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -175,7 +175,6 @@ typedef struct BDRVRawState {
} BDRVRawState;
typedef struct BDRVRawReopenState {
- int fd;
int open_flags;
bool drop_cache;
bool check_cache_dropped;
@@ -1075,7 +1074,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
BDRVRawReopenState *rs;
QemuOpts *opts;
int ret;
- Error *local_err = NULL;
assert(state != NULL);
assert(state->bs != NULL);
@@ -1101,32 +1099,18 @@ static int raw_reopen_prepare(BDRVReopenState *state,
* bdrv_reopen_prepare() will detect changes and complain. */
qemu_opts_to_qdict(opts, state->options);
- rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags,
- state->perm, true, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -1;
- goto out;
- }
-
- /* Fail already reopen_prepare() if we can't get a working O_DIRECT
- * alignment with the new fd. */
- if (rs->fd != -1) {
- raw_probe_alignment(state->bs, rs->fd, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
- goto out_fd;
- }
- }
+ /*
+ * As part of reopen prepare we also want to create new fd by
+ * raw_reconfigure_getfd(). But it wants updated "perm", when in
+ * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to
+ * permission update. Happily, permission update is always a part (a seprate
+ * stage) of bdrv_reopen_multiple() so we can rely on this fact and
+ * reconfigure fd in raw_check_perm().
+ */
s->reopen_state = state;
ret = 0;
-out_fd:
- if (ret < 0) {
- qemu_close(rs->fd);
- rs->fd = -1;
- }
+
out:
qemu_opts_del(opts);
return ret;
@@ -1140,10 +1124,6 @@ static void raw_reopen_commit(BDRVReopenState *state)
s->drop_cache = rs->drop_cache;
s->check_cache_dropped = rs->check_cache_dropped;
s->open_flags = rs->open_flags;
-
- qemu_close(s->fd);
- s->fd = rs->fd;
-
g_free(state->opaque);
state->opaque = NULL;
@@ -1162,10 +1142,6 @@ static void raw_reopen_abort(BDRVReopenState *state)
return;
}
- if (rs->fd >= 0) {
- qemu_close(rs->fd);
- rs->fd = -1;
- }
g_free(state->opaque);
state->opaque = NULL;
@@ -3073,39 +3049,30 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
Error **errp)
{
BDRVRawState *s = bs->opaque;
- BDRVRawReopenState *rs = NULL;
+ int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags;
int open_flags;
int ret;
- if (s->perm_change_fd) {
+ /* We may need a new fd if auto-read-only switches the mode */
+ ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm,
+ false, errp);
+ if (ret < 0) {
+ return ret;
+ } else if (ret != s->fd) {
+ Error *local_err = NULL;
+
/*
- * In the context of reopen, this function may be called several times
- * (directly and recursively while change permissions of the parent).
- * This is even true for children that don't inherit from the original
- * reopen node, so s->reopen_state is not set.
- *
- * Ignore all but the first call.
+ * Fail already check_perm() if we can't get a working O_DIRECT
+ * alignment with the new fd.
*/
- return 0;
- }
-
- if (s->reopen_state) {
- /* We already have a new file descriptor to set permissions for */
- assert(s->reopen_state->perm == perm);
- assert(s->reopen_state->shared_perm == shared);
- rs = s->reopen_state->opaque;
- s->perm_change_fd = rs->fd;
- s->perm_change_flags = rs->open_flags;
- } else {
- /* We may need a new fd if auto-read-only switches the mode */
- ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm,
- false, errp);
- if (ret < 0) {
- return ret;
- } else if (ret != s->fd) {
- s->perm_change_fd = ret;
- s->perm_change_flags = open_flags;
+ raw_probe_alignment(bs, ret, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
}
+
+ s->perm_change_fd = ret;
+ s->perm_change_flags = open_flags;
}
/* Prepare permissions on old fd to avoid conflicts between old and new,
@@ -3127,7 +3094,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared,
return 0;
fail:
- if (s->perm_change_fd && !s->reopen_state) {
+ if (s->perm_change_fd) {
qemu_close(s->perm_change_fd);
}
s->perm_change_fd = 0;
@@ -3158,7 +3125,7 @@ static void raw_abort_perm_update(BlockDriverState *bs)
/* For reopen, .bdrv_reopen_abort is called afterwards and will close
* the file descriptor. */
- if (s->perm_change_fd && !s->reopen_state) {
+ if (s->perm_change_fd) {
qemu_close(s->perm_change_fd);
}
s->perm_change_fd = 0;
diff --git a/block/io.c b/block/io.c
index ca2dca3007..35b6c56efc 100644
--- a/block/io.c
+++ b/block/io.c
@@ -133,13 +133,40 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src)
dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov);
}
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
+typedef struct BdrvRefreshLimitsState {
+ BlockDriverState *bs;
+ BlockLimits old_bl;
+} BdrvRefreshLimitsState;
+
+static void bdrv_refresh_limits_abort(void *opaque)
+{
+ BdrvRefreshLimitsState *s = opaque;
+
+ s->bs->bl = s->old_bl;
+}
+
+static TransactionActionDrv bdrv_refresh_limits_drv = {
+ .abort = bdrv_refresh_limits_abort,
+ .clean = g_free,
+};
+
+/* @tran is allowed to be NULL, in this case no rollback is possible. */
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp)
{
ERRP_GUARD();
BlockDriver *drv = bs->drv;
BdrvChild *c;
bool have_limits;
+ if (tran) {
+ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1);
+ *s = (BdrvRefreshLimitsState) {
+ .bs = bs,
+ .old_bl = bs->bl,
+ };
+ tran_add(tran, &bdrv_refresh_limits_drv, s);
+ }
+
memset(&bs->bl, 0, sizeof(bs->bl));
if (!drv) {
@@ -156,7 +183,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
QLIST_FOREACH(c, &bs->children, next) {
if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW))
{
- bdrv_refresh_limits(c->bs, errp);
+ bdrv_refresh_limits(c->bs, tran, errp);
if (*errp) {
return;
}
diff --git a/block/mirror.c b/block/mirror.c
index 5a71bd8bbc..840b8e8c15 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1630,9 +1630,6 @@ static BlockJob *mirror_start_job(
bs_opaque->is_commit = target_is_backing;
- /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep
- * it alive until block_job_create() succeeds even if bs has no parent. */
- bdrv_ref(mirror_top_bs);
bdrv_drained_begin(bs);
ret = bdrv_append(mirror_top_bs, bs, errp);
bdrv_drained_end(bs);
diff --git a/blockdev.c b/blockdev.c
index a57590aae4..834c2304a1 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1576,10 +1576,6 @@ static void external_snapshot_prepare(BlkActionState *common,
goto out;
}
- /* This removes our old bs and adds the new bs. This is an operation that
- * can fail, so we need to do it in .prepare; undoing it for abort is
- * always possible. */
- bdrv_ref(state->new_bs);
ret = bdrv_append(state->new_bs, state->old_bs, errp);
if (ret < 0) {
goto out;
diff --git a/blockjob.c b/blockjob.c
index 207e8c7fd9..2fe1d788ba 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -163,6 +163,13 @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx,
job->job.aio_context = ctx;
}
+static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
+{
+ BlockJob *job = c->opaque;
+
+ return job->job.aio_context;
+}
+
static const BdrvChildClass child_job = {
.get_parent_desc = child_job_get_parent_desc,
.drained_begin = child_job_drained_begin,
@@ -171,6 +178,7 @@ static const BdrvChildClass child_job = {
.can_set_aio_ctx = child_job_can_set_aio_ctx,
.set_aio_ctx = child_job_set_aio_ctx,
.stay_at_node = true,
+ .get_parent_aio_context = child_job_get_parent_aio_context,
};
void block_job_remove_all_bdrv(BlockJob *job)
@@ -221,8 +229,7 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
aio_context_release(job->job.aio_context);
}
- c = bdrv_root_attach_child(bs, name, &child_job, 0,
- job->job.aio_context, perm, shared_perm, job,
+ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job,
errp);
if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) {
aio_context_acquire(job->job.aio_context);
diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c
index f38c4faacf..e1ed3b7b60 100644
--- a/bsd-user/bsdload.c
+++ b/bsd-user/bsdload.c
@@ -13,22 +13,23 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src,
void *host_ptr;
host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
- if (!host_ptr)
+ if (!host_ptr) {
return -TARGET_EFAULT;
+ }
memcpy(host_ptr, src, len);
unlock_user(host_ptr, dest, 1);
return 0;
}
-static int count(char ** vec)
+static int count(char **vec)
{
int i;
- for(i = 0; *vec; i++) {
+ for (i = 0; *vec; i++) {
vec++;
}
- return(i);
+ return i;
}
static int prepare_binprm(struct linux_binprm *bprm)
@@ -37,23 +38,23 @@ static int prepare_binprm(struct linux_binprm *bprm)
int mode;
int retval;
- if(fstat(bprm->fd, &st) < 0) {
- return(-errno);
+ if (fstat(bprm->fd, &st) < 0) {
+ return -errno;
}
mode = st.st_mode;
- if(!S_ISREG(mode)) { /* Must be regular file */
- return(-EACCES);
+ if (!S_ISREG(mode)) { /* Must be regular file */
+ return -EACCES;
}
- if(!(mode & 0111)) { /* Must have at least one execute bit set */
- return(-EACCES);
+ if (!(mode & 0111)) { /* Must have at least one execute bit set */
+ return -EACCES;
}
bprm->e_uid = geteuid();
bprm->e_gid = getegid();
/* Set-uid? */
- if(mode & S_ISUID) {
+ if (mode & S_ISUID) {
bprm->e_uid = st.st_uid;
}
@@ -69,16 +70,14 @@ static int prepare_binprm(struct linux_binprm *bprm)
memset(bprm->buf, 0, sizeof(bprm->buf));
retval = lseek(bprm->fd, 0L, SEEK_SET);
- if(retval >= 0) {
+ if (retval >= 0) {
retval = read(bprm->fd, bprm->buf, 128);
}
- if(retval < 0) {
+ if (retval < 0) {
perror("prepare_binprm");
exit(-1);
- /* return(-errno); */
- }
- else {
- return(retval);
+ } else {
+ return retval;
}
}
@@ -125,19 +124,21 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
return sp;
}
-int loader_exec(const char * filename, char ** argv, char ** envp,
- struct target_pt_regs * regs, struct image_info *infop)
+int loader_exec(const char *filename, char **argv, char **envp,
+ struct target_pt_regs *regs, struct image_info *infop)
{
struct linux_binprm bprm;
int retval;
int i;
- bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
- for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
+ bprm.p = TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(unsigned int);
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) { /* clear page-table */
bprm.page[i] = NULL;
+ }
retval = open(filename, O_RDONLY);
- if (retval < 0)
+ if (retval < 0) {
return retval;
+ }
bprm.fd = retval;
bprm.filename = (char *)filename;
bprm.argc = count(argv);
@@ -147,27 +148,27 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
retval = prepare_binprm(&bprm);
- if(retval>=0) {
+ if (retval >= 0) {
if (bprm.buf[0] == 0x7f
&& bprm.buf[1] == 'E'
&& bprm.buf[2] == 'L'
&& bprm.buf[3] == 'F') {
- retval = load_elf_binary(&bprm,regs,infop);
+ retval = load_elf_binary(&bprm, regs, infop);
} else {
fprintf(stderr, "Unknown binary format\n");
return -1;
}
}
- if(retval>=0) {
+ if (retval >= 0) {
/* success. Initialize important registers */
do_init_thread(regs, infop);
return retval;
}
/* Something went wrong, return the inode and free the argument pages*/
- for (i=0 ; i<MAX_ARG_PAGES ; i++) {
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
g_free(bprm.page[i]);
}
- return(retval);
+ return retval;
}
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index d2bcaab741..b836b603af 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -233,7 +233,7 @@ static inline bool access_ok(int type, abi_ulong addr, abi_ulong size)
#define __put_user(x, hptr)\
({\
int size = sizeof(*hptr);\
- switch(size) {\
+ switch (size) {\
case 1:\
*(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\
break;\
@@ -255,7 +255,7 @@ static inline bool access_ok(int type, abi_ulong addr, abi_ulong size)
#define __get_user(x, hptr) \
({\
int size = sizeof(*hptr);\
- switch(size) {\
+ switch (size) {\
case 1:\
x = (typeof(*hptr))*(uint8_t *)(hptr);\
break;\
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index adc3d21b54..4abff796c7 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -199,6 +199,7 @@ static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
#else
case CTLTYPE_LONG:
*(uint64_t *)holdp = tswap64(*(long *)holdp);
+ break;
case CTLTYPE_ULONG:
*(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
break;
diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst
index 74f66bf46e..49ea50c2a7 100644
--- a/docs/devel/decodetree.rst
+++ b/docs/devel/decodetree.rst
@@ -40,9 +40,6 @@ and returns an integral value extracted from there.
A field with no ``unnamed_fields`` and no ``!function`` is in error.
-FIXME: the fields of the structure into which this result will be stored
-is restricted to ``int``. Which means that we cannot expand 64-bit items.
-
Field examples:
+---------------------------+---------------------------------------------+
@@ -66,9 +63,14 @@ Argument Sets
Syntax::
args_def := '&' identifier ( args_elt )+ ( !extern )?
- args_elt := identifier
+ args_elt := identifier (':' identifier)?
Each *args_elt* defines an argument within the argument set.
+If the form of the *args_elt* contains a colon, the first
+identifier is the argument name and the second identifier is
+the argument type. If the colon is missing, the argument
+type will be ``int``.
+
Each argument set will be rendered as a C structure "arg_$name"
with each of the fields being one of the member arguments.
@@ -86,6 +88,7 @@ Argument set examples::
&reg3 ra rb rc
&loadstore reg base offset
+ &longldst reg base offset:int64_t
Formats
diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
index b7a2d39c10..87c67ab23f 100644
--- a/docs/sphinx/qapidoc.py
+++ b/docs/sphinx/qapidoc.py
@@ -34,7 +34,8 @@ from sphinx.errors import ExtensionError
from sphinx.util.nodes import nested_parse_with_titles
import sphinx
from qapi.gen import QAPISchemaVisitor
-from qapi.schema import QAPIError, QAPISemError, QAPISchema
+from qapi.error import QAPIError, QAPISemError
+from qapi.schema import QAPISchema
# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later
diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc
index c2f87addb2..9ea318f3e2 100644
--- a/fpu/softfloat-specialize.c.inc
+++ b/fpu/softfloat-specialize.c.inc
@@ -145,6 +145,9 @@ static FloatParts parts_default_nan(float_status *status)
#elif defined(TARGET_HPPA)
/* snan_bit_is_one, set msb-1. */
frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2);
+#elif defined(TARGET_HEXAGON)
+ sign = 1;
+ frac = ~0ULL;
#else
/* This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V,
* S390, SH4, TriCore, and Xtensa. I cannot find documentation
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 8705612535..228dc54b0b 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -259,8 +259,9 @@ static void smmuv3_init_regs(SMMUv3State *s)
s->idr[3] = FIELD_DP32(s->idr[3], IDR3, RIL, 1);
s->idr[3] = FIELD_DP32(s->idr[3], IDR3, HAD, 1);
- /* 4K and 64K granule support */
+ /* 4K, 16K and 64K granule support */
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1);
+ s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN16K, 1);
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1);
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */
@@ -503,7 +504,8 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event)
tg = CD_TG(cd, i);
tt->granule_sz = tg2granule(tg, i);
- if ((tt->granule_sz != 12 && tt->granule_sz != 16) || CD_ENDI(cd)) {
+ if ((tt->granule_sz != 12 && tt->granule_sz != 14 &&
+ tt->granule_sz != 16) || CD_ENDI(cd)) {
goto bad_cd;
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 9f01d9041b..fee696fb0e 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2757,10 +2757,15 @@ static void machvirt_machine_init(void)
}
type_init(machvirt_machine_init);
+static void virt_machine_6_1_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
+
static void virt_machine_6_0_options(MachineClass *mc)
{
}
-DEFINE_VIRT_MACHINE_AS_LATEST(6, 0)
+DEFINE_VIRT_MACHINE(6, 0)
static void virt_machine_5_2_options(MachineClass *mc)
{
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 0b5b9d44cd..f5e9682703 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -467,6 +467,11 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
error_setg(errp, "vhost-user-blk: queue size must be non-zero");
return;
}
+ if (s->queue_size > VIRTQUEUE_MAX_SIZE) {
+ error_setg(errp, "vhost-user-blk: queue size must not exceed %d",
+ VIRTQUEUE_MAX_SIZE);
+ return;
+ }
if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) {
return;
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 40def78183..0f5ce43d0c 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -36,6 +36,11 @@
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-pci.h"
+GlobalProperty hw_compat_6_0[] = {
+ { "gpex-pcihost", "allow-unmapped-accesses", "false" },
+};
+const size_t hw_compat_6_0_len = G_N_ELEMENTS(hw_compat_6_0);
+
GlobalProperty hw_compat_5_2[] = {
{ "ICH9-LPC", "smm-compat", "on"},
{ "PIIX4_PM", "smm-compat", "on"},
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 8a84b25a03..364816efc9 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -96,6 +96,9 @@
#include "trace.h"
#include CONFIG_DEVICES
+GlobalProperty pc_compat_6_0[] = {};
+const size_t pc_compat_6_0_len = G_N_ELEMENTS(pc_compat_6_0);
+
GlobalProperty pc_compat_5_2[] = {
{ "ICH9-LPC", "x-smi-cpu-hotunplug", "off" },
};
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 46cc951073..4e8edffeaf 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -415,7 +415,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
}
-static void pc_i440fx_6_0_machine_options(MachineClass *m)
+static void pc_i440fx_6_1_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_machine_options(m);
@@ -424,6 +424,18 @@ static void pc_i440fx_6_0_machine_options(MachineClass *m)
pcmc->default_cpu_version = 1;
}
+DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
+ pc_i440fx_6_1_machine_options);
+
+static void pc_i440fx_6_0_machine_options(MachineClass *m)
+{
+ pc_i440fx_6_1_machine_options(m);
+ m->alias = NULL;
+ m->is_default = false;
+ compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len);
+ compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len);
+}
+
DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL,
pc_i440fx_6_0_machine_options);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 53450190f5..458ed41c65 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -345,7 +345,7 @@ static void pc_q35_machine_options(MachineClass *m)
m->max_cpus = 288;
}
-static void pc_q35_6_0_machine_options(MachineClass *m)
+static void pc_q35_6_1_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_machine_options(m);
@@ -353,6 +353,17 @@ static void pc_q35_6_0_machine_options(MachineClass *m)
pcmc->default_cpu_version = 1;
}
+DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
+ pc_q35_6_1_machine_options);
+
+static void pc_q35_6_0_machine_options(MachineClass *m)
+{
+ pc_q35_6_1_machine_options(m);
+ m->alias = NULL;
+ compat_props_add(m->compat_props, hw_compat_6_0, hw_compat_6_0_len);
+ compat_props_add(m->compat_props, pc_compat_6_0, pc_compat_6_0_len);
+}
+
DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL,
pc_q35_6_0_machine_options);
diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c
index b3b6a4378a..5ae3b4984d 100644
--- a/hw/isa/piix4.c
+++ b/hw/isa/piix4.c
@@ -268,8 +268,9 @@ DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus)
pci_create_simple(pci_bus, devfn + 2, "piix4-usb-uhci");
if (smbus) {
*smbus = piix4_pm_init(pci_bus, devfn + 3, 0x1100,
- isa_get_irq(NULL, 9), NULL, 0, NULL);
- }
+ qdev_get_gpio_in_named(dev, "isa", 9),
+ NULL, 0, NULL);
+ }
return dev;
}
diff --git a/hw/mips/meson.build b/hw/mips/meson.build
index 1195716dc7..dd0101ad4d 100644
--- a/hw/mips/meson.build
+++ b/hw/mips/meson.build
@@ -1,12 +1,15 @@
mips_ss = ss.source_set()
mips_ss.add(files('bootloader.c', 'mips_int.c'))
mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c'))
-mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c'))
-mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
+mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c'))
+
+if 'CONFIG_TCG' in config_all
+mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c'))
+mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt])
-mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c'))
+endif
hw_arch += {'mips': mips_ss}
diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
index 2bdbe7b456..a6752fac5e 100644
--- a/hw/pci-host/gpex.c
+++ b/hw/pci-host/gpex.c
@@ -83,12 +83,51 @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
int i;
pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
+ sysbus_init_mmio(sbd, &pex->mmio);
+
+ /*
+ * Note that the MemoryRegions io_mmio and io_ioport that we pass
+ * to pci_register_root_bus() are not the same as the
+ * MemoryRegions io_mmio_window and io_ioport_window that we
+ * expose as SysBus MRs. The difference is in the behaviour of
+ * accesses to addresses where no PCI device has been mapped.
+ *
+ * io_mmio and io_ioport are the underlying PCI view of the PCI
+ * address space, and when a PCI device does a bus master access
+ * to a bad address this is reported back to it as a transaction
+ * failure.
+ *
+ * io_mmio_window and io_ioport_window implement "unmapped
+ * addresses read as -1 and ignore writes"; this is traditional
+ * x86 PC behaviour, which is not mandated by the PCI spec proper
+ * but expected by much PCI-using guest software, including Linux.
+ *
+ * In the interests of not being unnecessarily surprising, we
+ * implement it in the gpex PCI host controller, by providing the
+ * _window MRs, which are containers with io ops that implement
+ * the 'background' behaviour and which hold the real PCI MRs as
+ * subregions.
+ */
memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX);
memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024);
- sysbus_init_mmio(sbd, &pex->mmio);
- sysbus_init_mmio(sbd, &s->io_mmio);
- sysbus_init_mmio(sbd, &s->io_ioport);
+ if (s->allow_unmapped_accesses) {
+ memory_region_init_io(&s->io_mmio_window, OBJECT(s),
+ &unassigned_io_ops, OBJECT(s),
+ "gpex_mmio_window", UINT64_MAX);
+ memory_region_init_io(&s->io_ioport_window, OBJECT(s),
+ &unassigned_io_ops, OBJECT(s),
+ "gpex_ioport_window", 64 * 1024);
+
+ memory_region_add_subregion(&s->io_mmio_window, 0, &s->io_mmio);
+ memory_region_add_subregion(&s->io_ioport_window, 0, &s->io_ioport);
+ sysbus_init_mmio(sbd, &s->io_mmio_window);
+ sysbus_init_mmio(sbd, &s->io_ioport_window);
+ } else {
+ sysbus_init_mmio(sbd, &s->io_mmio);
+ sysbus_init_mmio(sbd, &s->io_ioport);
+ }
+
for (i = 0; i < GPEX_NUM_IRQS; i++) {
sysbus_init_irq(sbd, &s->irq[i]);
s->irq_num[i] = -1;
@@ -108,6 +147,16 @@ static const char *gpex_host_root_bus_path(PCIHostState *host_bridge,
return "0000:00";
}
+static Property gpex_host_properties[] = {
+ /*
+ * Permit CPU accesses to unmapped areas of the PIO and MMIO windows
+ * (discarding writes and returning -1 for reads) rather than aborting.
+ */
+ DEFINE_PROP_BOOL("allow-unmapped-accesses", GPEXHost,
+ allow_unmapped_accesses, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void gpex_host_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -117,6 +166,7 @@ static void gpex_host_class_init(ObjectClass *klass, void *data)
dc->realize = gpex_host_realize;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->fw_name = "pci";
+ device_class_set_props(dc, gpex_host_properties);
}
static void gpex_host_initfn(Object *obj)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index e4be00b732..529ff056dd 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -4605,14 +4605,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
type_init(spapr_machine_register_##suffix)
/*
+ * pseries-6.1
+ */
+static void spapr_machine_6_1_class_options(MachineClass *mc)
+{
+ /* Defaults for the latest behaviour inherited from the base class */
+}
+
+DEFINE_SPAPR_MACHINE(6_1, "6.1", true);
+
+/*
* pseries-6.0
*/
static void spapr_machine_6_0_class_options(MachineClass *mc)
{
- /* Defaults for the latest behaviour inherited from the base class */
+ spapr_machine_6_1_class_options(mc);
+ compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len);
}
-DEFINE_SPAPR_MACHINE(6_0, "6.0", true);
+DEFINE_SPAPR_MACHINE(6_0, "6.0", false);
/*
* pseries-5.2
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 2972b607f3..56b52d2d30 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -795,14 +795,26 @@ bool css_migration_enabled(void)
} \
type_init(ccw_machine_register_##suffix)
+static void ccw_machine_6_1_instance_options(MachineState *machine)
+{
+}
+
+static void ccw_machine_6_1_class_options(MachineClass *mc)
+{
+}
+DEFINE_CCW_MACHINE(6_1, "6.1", true);
+
static void ccw_machine_6_0_instance_options(MachineState *machine)
{
+ ccw_machine_6_1_instance_options(machine);
}
static void ccw_machine_6_0_class_options(MachineClass *mc)
{
+ ccw_machine_6_1_class_options(mc);
+ compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len);
}
-DEFINE_CCW_MACHINE(6_0, "6.0", true);
+DEFINE_CCW_MACHINE(6_0, "6.0", false);
static void ccw_machine_5_2_instance_options(MachineState *machine)
{
diff --git a/include/block/block.h b/include/block/block.h
index b3f6e509d4..82185965ff 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -9,6 +9,7 @@
#include "block/dirty-bitmap.h"
#include "block/blockjob.h"
#include "qemu/hbitmap.h"
+#include "qemu/transactions.h"
/*
* generated_co_wrapper
@@ -101,6 +102,7 @@ typedef struct HDGeometry {
uint32_t cylinders;
} HDGeometry;
+#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */
#define BDRV_O_RDWR 0x0002
#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */
#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
@@ -207,8 +209,7 @@ typedef struct BDRVReopenState {
BlockdevDetectZeroesOptions detect_zeroes;
bool backing_missing;
bool replace_backing_bs; /* new_backing_bs is ignored if this is false */
- BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */
- uint64_t perm, shared_perm;
+ BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
QDict *options;
QDict *explicit_options;
void *opaque;
@@ -362,6 +363,7 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp);
BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options,
int flags, Error **errp);
+int bdrv_drop_filter(BlockDriverState *bs, Error **errp);
int bdrv_parse_aio(const char *mode, int *flags);
int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);
@@ -387,10 +389,6 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only,
Error **errp);
-int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
- BlockReopenQueue *queue, Error **errp);
-void bdrv_reopen_commit(BDRVReopenState *reopen_state);
-void bdrv_reopen_abort(BDRVReopenState *reopen_state);
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
int64_t bytes, BdrvRequestFlags flags);
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);
@@ -424,7 +422,7 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
BlockDriverState *in_bs, Error **errp);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
-void bdrv_refresh_limits(BlockDriverState *bs, Error **errp);
+void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp);
int bdrv_commit(BlockDriverState *bs);
int bdrv_make_empty(BdrvChild *c, Error **errp);
int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
@@ -702,6 +700,8 @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
GSList **ignore, Error **errp);
bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
GSList **ignore, Error **errp);
+AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
+
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 88e4111939..c823f5b1b3 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -789,6 +789,8 @@ struct BdrvChildClass {
bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx,
GSList **ignore, Error **errp);
void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore);
+
+ AioContext *(*get_parent_aio_context)(BdrvChild *child);
};
extern const BdrvChildClass child_of_bds;
@@ -811,11 +813,6 @@ struct BdrvChild {
*/
uint64_t shared_perm;
- /* backup of permissions during permission update procedure */
- bool has_backup_perm;
- uint64_t backup_perm;
- uint64_t backup_shared_perm;
-
/*
* This link is frozen: the child can neither be replaced nor
* detached from the parent.
@@ -1306,7 +1303,6 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
const BdrvChildClass *child_class,
BdrvChildRole child_role,
- AioContext *ctx,
uint64_t perm, uint64_t shared_perm,
void *opaque, Error **errp);
void bdrv_root_unref_child(BdrvChild *child);
diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h
index 29c02f85dc..1c2e7a8ed3 100644
--- a/include/exec/helper-gen.h
+++ b/include/exec/helper-gen.h
@@ -81,8 +81,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
#include "helper.h"
#include "trace/generated-helpers.h"
#include "trace/generated-helpers-wrappers.h"
-#include "tcg-runtime.h"
-#include "plugin-helpers.h"
+#include "accel/tcg/tcg-runtime.h"
+#include "accel/tcg/plugin-helpers.h"
#undef DEF_HELPER_FLAGS_0
#undef DEF_HELPER_FLAGS_1
diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h
index 659f9298e8..ba100793a7 100644
--- a/include/exec/helper-proto.h
+++ b/include/exec/helper-proto.h
@@ -39,8 +39,8 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \
#include "helper.h"
#include "trace/generated-helpers.h"
-#include "tcg-runtime.h"
-#include "plugin-helpers.h"
+#include "accel/tcg/tcg-runtime.h"
+#include "accel/tcg/plugin-helpers.h"
#undef IN_HELPER_PROTO
diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h
index 27870509a2..6888514635 100644
--- a/include/exec/helper-tcg.h
+++ b/include/exec/helper-tcg.h
@@ -60,8 +60,8 @@
#include "helper.h"
#include "trace/generated-helpers.h"
-#include "tcg-runtime.h"
-#include "plugin-helpers.h"
+#include "accel/tcg/tcg-runtime.h"
+#include "accel/tcg/plugin-helpers.h"
#undef str
#undef DEF_HELPER_FLAGS_0
diff --git a/include/hw/boards.h b/include/hw/boards.h
index ad6c8fd537..3d55d2bd62 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -353,6 +353,9 @@ struct MachineState {
} \
type_init(machine_initfn##_register_types)
+extern GlobalProperty hw_compat_6_0[];
+extern const size_t hw_compat_6_0_len;
+
extern GlobalProperty hw_compat_5_2[];
extern const size_t hw_compat_5_2_len;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index dcf060b791..1522a3359a 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -197,6 +197,9 @@ bool pc_system_ovmf_table_find(const char *entry, uint8_t **data,
void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
const CPUArchIdList *apic_ids, GArray *entry);
+extern GlobalProperty pc_compat_6_0[];
+extern const size_t pc_compat_6_0_len;
+
extern GlobalProperty pc_compat_5_2[];
extern const size_t pc_compat_5_2_len;
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
index d48a020a95..fcf8b63820 100644
--- a/include/hw/pci-host/gpex.h
+++ b/include/hw/pci-host/gpex.h
@@ -49,8 +49,12 @@ struct GPEXHost {
MemoryRegion io_ioport;
MemoryRegion io_mmio;
+ MemoryRegion io_ioport_window;
+ MemoryRegion io_mmio_window;
qemu_irq irq[GPEX_NUM_IRQS];
int irq_num[GPEX_NUM_IRQS];
+
+ bool allow_unmapped_accesses;
};
struct GPEXConfig {
diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h
new file mode 100644
index 0000000000..92c5965235
--- /dev/null
+++ b/include/qemu/transactions.h
@@ -0,0 +1,63 @@
+/*
+ * Simple transactions API
+ *
+ * Copyright (c) 2021 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+ *
+ * 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/>.
+ *
+ *
+ * = Generic transaction API =
+ *
+ * The intended usage is the following: you create "prepare" functions, which
+ * represents the actions. They will usually have Transaction* argument, and
+ * call tran_add() to register finalization callbacks. For finalization
+ * callbacks, prepare corresponding TransactionActionDrv structures.
+ *
+ * Then, when you need to make a transaction, create an empty Transaction by
+ * tran_create(), call your "prepare" functions on it, and finally call
+ * tran_abort() or tran_commit() to finalize the transaction by corresponding
+ * finalization actions in reverse order.
+ */
+
+#ifndef QEMU_TRANSACTIONS_H
+#define QEMU_TRANSACTIONS_H
+
+#include <gmodule.h>
+
+typedef struct TransactionActionDrv {
+ void (*abort)(void *opaque);
+ void (*commit)(void *opaque);
+ void (*clean)(void *opaque);
+} TransactionActionDrv;
+
+typedef struct Transaction Transaction;
+
+Transaction *tran_new(void);
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque);
+void tran_abort(Transaction *tran);
+void tran_commit(Transaction *tran);
+
+static inline void tran_finalize(Transaction *tran, int ret)
+{
+ if (ret < 0) {
+ tran_abort(tran);
+ } else {
+ tran_commit(tran);
+ }
+}
+
+#endif /* QEMU_TRANSACTIONS_H */
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c
index 9a68ca05c3..bc34f5d7c3 100644
--- a/linux-user/hexagon/cpu_loop.c
+++ b/linux-user/hexagon/cpu_loop.c
@@ -25,7 +25,7 @@
void cpu_loop(CPUHexagonState *env)
{
- CPUState *cs = CPU(hexagon_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
int trapnr, signum, sigcode;
target_ulong sigaddr;
target_ulong syscallnum;
diff --git a/meson.build b/meson.build
index c6f4b0cf5e..1ffdc9e6c4 100644
--- a/meson.build
+++ b/meson.build
@@ -258,7 +258,6 @@ if not get_option('tcg').disabled()
tcg_arch = 'riscv'
endif
add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch,
- '-iquote', meson.current_source_dir() / 'accel/tcg',
language: ['c', 'cpp', 'objc'])
accelerators += 'CONFIG_TCG'
@@ -1752,6 +1751,7 @@ modules = {}
hw_arch = {}
target_arch = {}
target_softmmu_arch = {}
+target_user_arch = {}
###############
# Trace files #
@@ -2169,6 +2169,11 @@ foreach target : target_dirs
abi = config_target['TARGET_ABI_DIR']
target_type='user'
qemu_target_name = 'qemu-' + target_name
+ if arch in target_user_arch
+ t = target_user_arch[arch].apply(config_target, strict: false)
+ arch_srcs += t.sources()
+ arch_deps += t.dependencies()
+ endif
if 'CONFIG_LINUX_USER' in config_target
base_dir = 'linux-user'
target_inc += include_directories('linux-user/host/' / config_host['ARCH'])
diff --git a/qemu-img.c b/qemu-img.c
index babb5573ab..a5993682aa 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -2146,7 +2146,7 @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
static int img_convert(int argc, char **argv)
{
- int c, bs_i, flags, src_flags = 0;
+ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE;
const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe",
*src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL,
*out_filename, *out_baseimg_param, *snapshot_name = NULL;
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index 4637b633e7..a03dc6b5e3 100644
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -27,6 +27,7 @@ import sys
import getopt
insnwidth = 32
+bitop_width = 32
insnmask = 0xffffffff
variablewidth = False
fields = {}
@@ -59,9 +60,9 @@ def error_with_file(file, lineno, *args):
prefix = ''
if file:
- prefix += '{0}:'.format(file)
+ prefix += f'{file}:'
if lineno:
- prefix += '{0}:'.format(lineno)
+ prefix += f'{lineno}:'
if prefix:
prefix += ' '
print(prefix, end='error: ', file=sys.stderr)
@@ -102,6 +103,23 @@ def str_fields(fields):
return r[1:]
+def whex(val):
+ """Return a hex string for val padded for insnwidth"""
+ global insnwidth
+ return f'0x{val:0{insnwidth // 4}x}'
+
+
+def whexC(val):
+ """Return a hex string for val padded for insnwidth,
+ and with the proper suffix for a C constant."""
+ suffix = ''
+ if val >= 0x100000000:
+ suffix = 'ull'
+ elif val >= 0x80000000:
+ suffix = 'u'
+ return whex(val) + suffix
+
+
def str_match_bits(bits, mask):
"""Return a string pretty-printing BITS/MASK"""
global insnwidth
@@ -147,11 +165,15 @@ def is_contiguous(bits):
return -1
-def eq_fields_for_args(flds_a, flds_b):
- if len(flds_a) != len(flds_b):
+def eq_fields_for_args(flds_a, arg):
+ if len(flds_a) != len(arg.fields):
return False
+ # Only allow inference on default types
+ for t in arg.types:
+ if t != 'int':
+ return False
for k, a in flds_a.items():
- if k not in flds_b:
+ if k not in arg.fields:
return False
return True
@@ -184,11 +206,9 @@ class Field:
return str(self.pos) + ':' + s + str(self.len)
def str_extract(self):
- if self.sign:
- extr = 'sextract32'
- else:
- extr = 'extract32'
- return '{0}(insn, {1}, {2})'.format(extr, self.pos, self.len)
+ global bitop_width
+ s = 's' if self.sign else ''
+ return f'{s}extract{bitop_width}(insn, {self.pos}, {self.len})'
def __eq__(self, other):
return self.sign == other.sign and self.mask == other.mask
@@ -209,14 +229,15 @@ class MultiField:
return str(self.subs)
def str_extract(self):
+ global bitop_width
ret = '0'
pos = 0
for f in reversed(self.subs):
+ ext = f.str_extract()
if pos == 0:
- ret = f.str_extract()
+ ret = ext
else:
- ret = 'deposit32({0}, {1}, {2}, {3})' \
- .format(ret, pos, 32 - pos, f.str_extract())
+ ret = f'deposit{bitop_width}({ret}, {pos}, {bitop_width - pos}, {ext})'
pos += f.len
return ret
@@ -296,10 +317,11 @@ class ParameterField:
class Arguments:
"""Class representing the extracted fields of a format"""
- def __init__(self, nm, flds, extern):
+ def __init__(self, nm, flds, types, extern):
self.name = nm
self.extern = extern
- self.fields = sorted(flds)
+ self.fields = flds
+ self.types = types
def __str__(self):
return self.name + ' ' + str(self.fields)
@@ -310,8 +332,8 @@ class Arguments:
def output_def(self):
if not self.extern:
output('typedef struct {\n')
- for n in self.fields:
- output(' int ', n, ';\n')
+ for (n, t) in zip(self.fields, self.types):
+ output(f' {t} {n};\n')
output('} ', self.struct_name(), ';\n\n')
# end Arguments
@@ -477,11 +499,8 @@ class IncMultiPattern(MultiPattern):
if outermask != p.fixedmask:
innermask = p.fixedmask & ~outermask
innerbits = p.fixedbits & ~outermask
- output(ind, 'if ((insn & ',
- '0x{0:08x}) == 0x{1:08x}'.format(innermask, innerbits),
- ') {\n')
- output(ind, ' /* ',
- str_match_bits(p.fixedbits, p.fixedmask), ' */\n')
+ output(ind, f'if ((insn & {whexC(innermask)}) == {whexC(innerbits)}) {{\n')
+ output(ind, f' /* {str_match_bits(p.fixedbits, p.fixedmask)} */\n')
p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask)
output(ind, '}\n')
else:
@@ -500,12 +519,12 @@ class Tree:
def str1(self, i):
ind = str_indent(i)
- r = '{0}{1:08x}'.format(ind, self.fixedmask)
+ r = ind + whex(self.fixedmask)
if self.format:
r += ' ' + self.format.name
r += ' [\n'
for (b, s) in self.subs:
- r += '{0} {1:08x}:\n'.format(ind, b)
+ r += ind + f' {whex(b)}:\n'
r += s.str1(i + 4) + '\n'
r += ind + ']'
return r
@@ -529,16 +548,16 @@ class Tree:
if sh > 0:
# Propagate SH down into the local functions.
def str_switch(b, sh=sh):
- return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh)
+ return f'(insn >> {sh}) & {b >> sh:#x}'
def str_case(b, sh=sh):
- return '0x{0:x}'.format(b >> sh)
+ return hex(b >> sh)
else:
def str_switch(b):
- return 'insn & 0x{0:08x}'.format(b)
+ return f'insn & {whexC(b)}'
def str_case(b):
- return '0x{0:08x}'.format(b)
+ return whexC(b)
output(ind, 'switch (', str_switch(self.thismask), ') {\n')
for b, s in sorted(self.subs):
@@ -663,11 +682,11 @@ def parse_field(lineno, name, toks):
subtoks = t.split(':')
sign = False
else:
- error(lineno, 'invalid field token "{0}"'.format(t))
+ error(lineno, f'invalid field token "{t}"')
po = int(subtoks[0])
le = int(subtoks[1])
if po + le > insnwidth:
- error(lineno, 'field {0} too large'.format(t))
+ error(lineno, f'field {t} too large')
f = Field(sign, po, le)
subs.append(f)
width += le
@@ -705,21 +724,27 @@ def parse_arguments(lineno, name, toks):
global anyextern
flds = []
+ types = []
extern = False
- for t in toks:
- if re.fullmatch('!extern', t):
+ for n in toks:
+ if re.fullmatch('!extern', n):
extern = True
anyextern = True
continue
- if not re.fullmatch(re_C_ident, t):
- error(lineno, 'invalid argument set token "{0}"'.format(t))
- if t in flds:
- error(lineno, 'duplicate argument "{0}"'.format(t))
- flds.append(t)
+ if re.fullmatch(re_C_ident + ':' + re_C_ident, n):
+ (n, t) = n.split(':')
+ elif re.fullmatch(re_C_ident, n):
+ t = 'int'
+ else:
+ error(lineno, f'invalid argument set token "{n}"')
+ if n in flds:
+ error(lineno, f'duplicate argument "{n}"')
+ flds.append(n)
+ types.append(t)
if name in arguments:
error(lineno, 'duplicate argument set', name)
- arguments[name] = Arguments(name, flds, extern)
+ arguments[name] = Arguments(name, flds, types, extern)
# end parse_arguments
@@ -746,11 +771,11 @@ def infer_argument_set(flds):
global decode_function
for arg in arguments.values():
- if eq_fields_for_args(flds, arg.fields):
+ if eq_fields_for_args(flds, arg):
return arg
name = decode_function + str(len(arguments))
- arg = Arguments(name, flds.keys(), False)
+ arg = Arguments(name, flds.keys(), ['int'] * len(flds), False)
arguments[name] = arg
return arg
@@ -883,14 +908,14 @@ def parse_generic(lineno, parent_pat, name, toks):
flen = flen[1:]
shift = int(flen, 10)
if shift + width > insnwidth:
- error(lineno, 'field {0} exceeds insnwidth'.format(fname))
+ error(lineno, f'field {fname} exceeds insnwidth')
f = Field(sign, insnwidth - width - shift, shift)
flds = add_field(lineno, flds, fname, f)
fixedbits <<= shift
fixedmask <<= shift
undefmask <<= shift
else:
- error(lineno, 'invalid token "{0}"'.format(t))
+ error(lineno, f'invalid token "{t}"')
width += shift
if variablewidth and width < insnwidth and width % 8 == 0:
@@ -902,7 +927,7 @@ def parse_generic(lineno, parent_pat, name, toks):
# We should have filled in all of the bits of the instruction.
elif not (is_format and width == 0) and width != insnwidth:
- error(lineno, 'definition has {0} bits'.format(width))
+ error(lineno, f'definition has {width} bits')
# Do not check for fields overlapping fields; one valid usage
# is to be able to duplicate fields via import.
@@ -920,8 +945,7 @@ def parse_generic(lineno, parent_pat, name, toks):
if arg:
for f in flds.keys():
if f not in arg.fields:
- error(lineno, 'field {0} not in argument set {1}'
- .format(f, arg.name))
+ error(lineno, f'field {f} not in argument set {arg.name}')
else:
arg = infer_argument_set(flds)
if name in formats:
@@ -948,13 +972,12 @@ def parse_generic(lineno, parent_pat, name, toks):
arg = fmt.base
for f in flds.keys():
if f not in arg.fields:
- error(lineno, 'field {0} not in argument set {1}'
- .format(f, arg.name))
+ error(lineno, f'field {f} not in argument set {arg.name}')
if f in fmt.fields.keys():
- error(lineno, 'field {0} set by format and pattern'.format(f))
+ error(lineno, f'field {f} set by format and pattern')
for f in arg.fields:
if f not in flds.keys() and f not in fmt.fields.keys():
- error(lineno, 'field {0} not initialized'.format(f))
+ error(lineno, f'field {f} not initialized')
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
undefmask, fieldmask, flds, width)
parent_pat.pats.append(pat)
@@ -962,19 +985,19 @@ def parse_generic(lineno, parent_pat, name, toks):
# Validate the masks that we have assembled.
if fieldmask & fixedmask:
- error(lineno, 'fieldmask overlaps fixedmask (0x{0:08x} & 0x{1:08x})'
- .format(fieldmask, fixedmask))
+ error(lineno, 'fieldmask overlaps fixedmask ',
+ f'({whex(fieldmask)} & {whex(fixedmask)})')
if fieldmask & undefmask:
- error(lineno, 'fieldmask overlaps undefmask (0x{0:08x} & 0x{1:08x})'
- .format(fieldmask, undefmask))
+ error(lineno, 'fieldmask overlaps undefmask ',
+ f'({whex(fieldmask)} & {whex(undefmask)})')
if fixedmask & undefmask:
- error(lineno, 'fixedmask overlaps undefmask (0x{0:08x} & 0x{1:08x})'
- .format(fixedmask, undefmask))
+ error(lineno, 'fixedmask overlaps undefmask ',
+ f'({whex(fixedmask)} & {whex(undefmask)})')
if not is_format:
allbits = fieldmask | fixedmask | undefmask
if allbits != insnmask:
- error(lineno, 'bits left unspecified (0x{0:08x})'
- .format(allbits ^ insnmask))
+ error(lineno, 'bits left unspecified ',
+ f'({whex(allbits ^ insnmask)})')
# end parse_general
@@ -1085,7 +1108,7 @@ def parse_file(f, parent_pat):
elif re.fullmatch(re_pat_ident, name):
parse_generic(start_lineno, parent_pat, name, toks)
else:
- error(lineno, 'invalid token "{0}"'.format(name))
+ error(lineno, f'invalid token "{name}"')
toks = []
if nesting != 0:
@@ -1104,10 +1127,9 @@ class SizeTree:
def str1(self, i):
ind = str_indent(i)
- r = '{0}{1:08x}'.format(ind, self.mask)
- r += ' [\n'
+ r = ind + whex(self.mask) + ' [\n'
for (b, s) in self.subs:
- r += '{0} {1:08x}:\n'.format(ind, b)
+ r += ind + f' {whex(b)}:\n'
r += s.str1(i + 4) + '\n'
r += ind + ']'
return r
@@ -1120,9 +1142,8 @@ class SizeTree:
# If we need to load more bytes to test, do so now.
if extracted < self.width:
- output(ind, 'insn = ', decode_function,
- '_load_bytes(ctx, insn, {0}, {1});\n'
- .format(extracted // 8, self.width // 8));
+ output(ind, f'insn = {decode_function}_load_bytes',
+ f'(ctx, insn, {extracted // 8}, {self.width // 8});\n')
extracted = self.width
# Attempt to aid the compiler in producing compact switch statements.
@@ -1131,16 +1152,16 @@ class SizeTree:
if sh > 0:
# Propagate SH down into the local functions.
def str_switch(b, sh=sh):
- return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh)
+ return f'(insn >> {sh}) & {b >> sh:#x}'
def str_case(b, sh=sh):
- return '0x{0:x}'.format(b >> sh)
+ return hex(b >> sh)
else:
def str_switch(b):
- return 'insn & 0x{0:08x}'.format(b)
+ return f'insn & {whexC(b)}'
def str_case(b):
- return '0x{0:08x}'.format(b)
+ return whexC(b)
output(ind, 'switch (', str_switch(self.mask), ') {\n')
for b, s in sorted(self.subs):
@@ -1162,8 +1183,7 @@ class SizeLeaf:
self.width = w
def str1(self, i):
- ind = str_indent(i)
- return '{0}{1:08x}'.format(ind, self.mask)
+ return str_indent(i) + whex(self.mask)
def __str__(self):
return self.str1(0)
@@ -1174,9 +1194,8 @@ class SizeLeaf:
# If we need to load more bytes, do so now.
if extracted < self.width:
- output(ind, 'insn = ', decode_function,
- '_load_bytes(ctx, insn, {0}, {1});\n'
- .format(extracted // 8, self.width // 8));
+ output(ind, f'insn = {decode_function}_load_bytes',
+ f'(ctx, insn, {extracted // 8}, {self.width // 8});\n')
extracted = self.width
output(ind, 'return insn;\n')
# end SizeLeaf
@@ -1210,7 +1229,7 @@ def build_size_tree(pats, width, outerbits, outermask):
for p in pats:
pnames.append(p.name + ':' + p.file + ':' + str(p.lineno))
error_with_file(pats[0].file, pats[0].lineno,
- 'overlapping patterns size {0}:'.format(width), pnames)
+ f'overlapping patterns size {width}:', pnames)
bins = {}
for i in pats:
@@ -1264,6 +1283,7 @@ def main():
global insntype
global insnmask
global decode_function
+ global bitop_width
global variablewidth
global anyextern
@@ -1293,6 +1313,10 @@ def main():
if insnwidth == 16:
insntype = 'uint16_t'
insnmask = 0xffff
+ elif insnwidth == 64:
+ insntype = 'uint64_t'
+ insnmask = 0xffffffffffffffff
+ bitop_width = 64
elif insnwidth != 32:
error(0, 'cannot handle insns of width', insnwidth)
else:
diff --git a/scripts/qapi/error.py b/scripts/qapi/error.py
index ae60d9e2fe..e35e4ddb26 100644
--- a/scripts/qapi/error.py
+++ b/scripts/qapi/error.py
@@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
#
-# QAPI error classes
-#
# Copyright (c) 2017-2019 Red Hat Inc.
#
# Authors:
@@ -11,15 +9,36 @@
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
+"""
+QAPI error classes
+
+Common error classes used throughout the package. Additional errors may
+be defined in other modules. At present, `QAPIParseError` is defined in
+parser.py.
+"""
+
+from typing import Optional
+
+from .source import QAPISourceInfo
+
class QAPIError(Exception):
- def __init__(self, info, col, msg):
- Exception.__init__(self)
+ """Base class for all exceptions from the QAPI package."""
+
+
+class QAPISourceError(QAPIError):
+ """Error class for all exceptions identifying a source location."""
+ def __init__(self,
+ info: Optional[QAPISourceInfo],
+ msg: str,
+ col: Optional[int] = None):
+ super().__init__()
self.info = info
- self.col = col
self.msg = msg
+ self.col = col
- def __str__(self):
+ def __str__(self) -> str:
+ assert self.info is not None
loc = str(self.info)
if self.col is not None:
assert self.info.line is not None
@@ -27,17 +46,5 @@ class QAPIError(Exception):
return loc + ': ' + self.msg
-class QAPIParseError(QAPIError):
- def __init__(self, parser, msg):
- col = 1
- for ch in parser.src[parser.line_pos:parser.pos]:
- if ch == '\t':
- col = (col + 7) % 8 + 1
- else:
- col += 1
- super().__init__(parser.info, col, msg)
-
-
-class QAPISemError(QAPIError):
- def __init__(self, info, msg):
- super().__init__(info, None, msg)
+class QAPISemError(QAPISourceError):
+ """Error class for semantic QAPI errors."""
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 540b3982b1..496f7e0333 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -1,40 +1,97 @@
# -*- coding: utf-8 -*-
#
-# Check (context-free) QAPI schema expression structure
-#
# Copyright IBM, Corp. 2011
-# Copyright (c) 2013-2019 Red Hat Inc.
+# Copyright (c) 2013-2021 Red Hat Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
# Markus Armbruster <armbru@redhat.com>
# Eric Blake <eblake@redhat.com>
# Marc-André Lureau <marcandre.lureau@redhat.com>
+# John Snow <jsnow@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
-from collections import OrderedDict
+"""
+Normalize and validate (context-free) QAPI schema expression structures.
+
+`QAPISchemaParser` parses a QAPI schema into abstract syntax trees
+consisting of dict, list, str, bool, and int nodes. This module ensures
+that these nested structures have the correct type(s) and key(s) where
+appropriate for the QAPI context-free grammar.
+
+The QAPI schema expression language allows for certain syntactic sugar;
+this module also handles the normalization process of these nested
+structures.
+
+See `check_exprs` for the main entry point.
+
+See `schema.QAPISchema` for processing into native Python data
+structures and contextual semantic validation.
+"""
+
import re
+from typing import (
+ Collection,
+ Dict,
+ Iterable,
+ List,
+ Optional,
+ Union,
+ cast,
+)
from .common import c_name
from .error import QAPISemError
+from .parser import QAPIDoc
+from .source import QAPISourceInfo
-# Names consist of letters, digits, -, and _, starting with a letter.
-# An experimental name is prefixed with x-. A name of a downstream
-# extension is prefixed with __RFQDN_. The latter prefix goes first.
+# Deserialized JSON objects as returned by the parser.
+# The values of this mapping are not necessary to exhaustively type
+# here (and also not practical as long as mypy lacks recursive
+# types), because the purpose of this module is to interrogate that
+# type.
+_JSONObject = Dict[str, object]
+
+
+# See check_name_str(), below.
valid_name = re.compile(r'(__[a-z0-9.-]+_)?'
r'(x-)?'
r'([a-z][a-z0-9_-]*)$', re.IGNORECASE)
-def check_name_is_str(name, info, source):
+def check_name_is_str(name: object,
+ info: QAPISourceInfo,
+ source: str) -> None:
+ """
+ Ensure that ``name`` is a ``str``.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ """
if not isinstance(name, str):
raise QAPISemError(info, "%s requires a string name" % source)
-def check_name_str(name, info, source):
+def check_name_str(name: str, info: QAPISourceInfo, source: str) -> str:
+ """
+ Ensure that ``name`` is a valid QAPI name.
+
+ A valid name consists of ASCII letters, digits, ``-``, and ``_``,
+ starting with a letter. It may be prefixed by a downstream prefix
+ of the form __RFQDN_, or the experimental prefix ``x-``. If both
+ prefixes are present, the __RFDQN_ prefix goes first.
+
+ A valid name cannot start with ``q_``, which is reserved.
+
+ :param name: Name to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing what ``name`` belongs to.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ :return: The stem of the valid name, with no prefixes.
+ """
# Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
# and 'q_obj_*' implicit type names.
match = valid_name.match(name)
@@ -43,16 +100,44 @@ def check_name_str(name, info, source):
return match.group(3)
-def check_name_upper(name, info, source):
+def check_name_upper(name: str, info: QAPISourceInfo, source: str) -> None:
+ """
+ Ensure that ``name`` is a valid event name.
+
+ This means it must be a valid QAPI name as checked by
+ `check_name_str()`, but where the stem prohibits lowercase
+ characters and ``-``.
+
+ :param name: Name to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing what ``name`` belongs to.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ """
stem = check_name_str(name, info, source)
if re.search(r'[a-z-]', stem):
raise QAPISemError(
info, "name of %s must not use lowercase or '-'" % source)
-def check_name_lower(name, info, source,
- permit_upper=False,
- permit_underscore=False):
+def check_name_lower(name: str, info: QAPISourceInfo, source: str,
+ permit_upper: bool = False,
+ permit_underscore: bool = False) -> None:
+ """
+ Ensure that ``name`` is a valid command or member name.
+
+ This means it must be a valid QAPI name as checked by
+ `check_name_str()`, but where the stem prohibits uppercase
+ characters and ``_``.
+
+ :param name: Name to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing what ``name`` belongs to.
+ :param permit_upper: Additionally permit uppercase.
+ :param permit_underscore: Additionally permit ``_``.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ """
stem = check_name_str(name, info, source)
if ((not permit_upper and re.search(r'[A-Z]', stem))
or (not permit_underscore and '_' in stem)):
@@ -60,13 +145,40 @@ def check_name_lower(name, info, source,
info, "name of %s must not use uppercase or '_'" % source)
-def check_name_camel(name, info, source):
+def check_name_camel(name: str, info: QAPISourceInfo, source: str) -> None:
+ """
+ Ensure that ``name`` is a valid user-defined type name.
+
+ This means it must be a valid QAPI name as checked by
+ `check_name_str()`, but where the stem must be in CamelCase.
+
+ :param name: Name to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing what ``name`` belongs to.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ """
stem = check_name_str(name, info, source)
if not re.match(r'[A-Z][A-Za-z0-9]*[a-z][A-Za-z0-9]*$', stem):
raise QAPISemError(info, "name of %s must use CamelCase" % source)
-def check_defn_name_str(name, info, meta):
+def check_defn_name_str(name: str, info: QAPISourceInfo, meta: str) -> None:
+ """
+ Ensure that ``name`` is a valid definition name.
+
+ Based on the value of ``meta``, this means that:
+ - 'event' names adhere to `check_name_upper()`.
+ - 'command' names adhere to `check_name_lower()`.
+ - Else, meta is a type, and must pass `check_name_camel()`.
+ These names must not end with ``Kind`` nor ``List``.
+
+ :param name: Name to check.
+ :param info: QAPI schema source file information.
+ :param meta: Meta-type name of the QAPI expression.
+
+ :raise QAPISemError: When ``name`` fails validation.
+ """
if meta == 'event':
check_name_upper(name, info, meta)
elif meta == 'command':
@@ -75,14 +187,29 @@ def check_defn_name_str(name, info, meta):
permit_underscore=name in info.pragma.command_name_exceptions)
else:
check_name_camel(name, info, meta)
- if name.endswith('Kind') or name.endswith('List'):
- raise QAPISemError(
- info, "%s name should not end in '%s'" % (meta, name[-4:]))
+ if name.endswith('Kind') or name.endswith('List'):
+ raise QAPISemError(
+ info, "%s name should not end in '%s'" % (meta, name[-4:]))
+
+def check_keys(value: _JSONObject,
+ info: QAPISourceInfo,
+ source: str,
+ required: Collection[str],
+ optional: Collection[str]) -> None:
+ """
+ Ensure that a dict has a specific set of keys.
-def check_keys(value, info, source, required, optional):
+ :param value: The dict to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing this ``value``.
+ :param required: Keys that *must* be present.
+ :param optional: Keys that *may* be present.
- def pprint(elems):
+ :raise QAPISemError: When unknown keys are present.
+ """
+
+ def pprint(elems: Iterable[str]) -> str:
return ', '.join("'" + e + "'" for e in sorted(elems))
missing = set(required) - set(value)
@@ -92,7 +219,7 @@ def check_keys(value, info, source, required, optional):
"%s misses key%s %s"
% (source, 's' if len(missing) > 1 else '',
pprint(missing)))
- allowed = set(required + optional)
+ allowed = set(required) | set(optional)
unknown = set(value) - allowed
if unknown:
raise QAPISemError(
@@ -102,12 +229,22 @@ def check_keys(value, info, source, required, optional):
pprint(unknown), pprint(allowed)))
-def check_flags(expr, info):
- for key in ['gen', 'success-response']:
+def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Ensure flag members (if present) have valid values.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError:
+ When certain flags have an invalid value, or when
+ incompatible flags are present.
+ """
+ for key in ('gen', 'success-response'):
if key in expr and expr[key] is not False:
raise QAPISemError(
info, "flag '%s' may only use false value" % key)
- for key in ['boxed', 'allow-oob', 'allow-preconfig', 'coroutine']:
+ for key in ('boxed', 'allow-oob', 'allow-preconfig', 'coroutine'):
if key in expr and expr[key] is not True:
raise QAPISemError(
info, "flag '%s' may only use true value" % key)
@@ -120,47 +257,106 @@ def check_flags(expr, info):
"are incompatible")
-def check_if(expr, info, source):
+def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
+ """
+ Normalize and validate the ``if`` member of an object.
- def check_if_str(ifcond, info):
- if not isinstance(ifcond, str):
- raise QAPISemError(
- info,
- "'if' condition of %s must be a string or a list of strings"
- % source)
- if ifcond.strip() == '':
- raise QAPISemError(
- info,
- "'if' condition '%s' of %s makes no sense"
- % (ifcond, source))
+ The ``if`` member may be either a ``str`` or a ``List[str]``.
+ A ``str`` value will be normalized to ``List[str]``.
+ :forms:
+ :sugared: ``Union[str, List[str]]``
+ :canonical: ``List[str]``
+
+ :param expr: The expression containing the ``if`` member to validate.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing ``expr``.
+
+ :raise QAPISemError:
+ When the "if" member fails validation, or when there are no
+ non-empty conditions.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
ifcond = expr.get('if')
if ifcond is None:
return
+
if isinstance(ifcond, list):
- if ifcond == []:
+ if not ifcond:
raise QAPISemError(
info, "'if' condition [] of %s is useless" % source)
- for elt in ifcond:
- check_if_str(elt, info)
else:
- check_if_str(ifcond, info)
- expr['if'] = [ifcond]
+ # Normalize to a list
+ ifcond = expr['if'] = [ifcond]
+
+ for elt in ifcond:
+ if not isinstance(elt, str):
+ raise QAPISemError(
+ info,
+ "'if' condition of %s must be a string or a list of strings"
+ % source)
+ if not elt.strip():
+ raise QAPISemError(
+ info,
+ "'if' condition '%s' of %s makes no sense"
+ % (elt, source))
-def normalize_members(members):
- if isinstance(members, OrderedDict):
+def normalize_members(members: object) -> None:
+ """
+ Normalize a "members" value.
+
+ If ``members`` is a dict, for every value in that dict, if that
+ value is not itself already a dict, normalize it to
+ ``{'type': value}``.
+
+ :forms:
+ :sugared: ``Dict[str, Union[str, TypeRef]]``
+ :canonical: ``Dict[str, TypeRef]``
+
+ :param members: The members value to normalize.
+
+ :return: None, ``members`` is normalized in-place as needed.
+ """
+ if isinstance(members, dict):
for key, arg in members.items():
if isinstance(arg, dict):
continue
members[key] = {'type': arg}
-def check_type(value, info, source,
- allow_array=False, allow_dict=False):
+def check_type(value: Optional[object],
+ info: QAPISourceInfo,
+ source: str,
+ allow_array: bool = False,
+ allow_dict: Union[bool, str] = False) -> None:
+ """
+ Normalize and validate the QAPI type of ``value``.
+
+ Python types of ``str`` or ``None`` are always allowed.
+
+ :param value: The value to check.
+ :param info: QAPI schema source file information.
+ :param source: Error string describing this ``value``.
+ :param allow_array:
+ Allow a ``List[str]`` of length 1, which indicates an array of
+ the type named by the list element.
+ :param allow_dict:
+ Allow a dict. Its members can be struct type members or union
+ branches. When the value of ``allow_dict`` is in pragma
+ ``member-name-exceptions``, the dict's keys may violate the
+ member naming rules. The dict members are normalized in place.
+
+ :raise QAPISemError: When ``value`` fails validation.
+ :return: None, ``value`` is normalized in-place as needed.
+ """
if value is None:
return
+ # Type name
+ if isinstance(value, str):
+ return
+
# Array type
if isinstance(value, list):
if not allow_array:
@@ -171,20 +367,18 @@ def check_type(value, info, source,
source)
return
- # Type name
- if isinstance(value, str):
- return
-
# Anonymous type
if not allow_dict:
raise QAPISemError(info, "%s should be a type name" % source)
- if not isinstance(value, OrderedDict):
+ if not isinstance(value, dict):
raise QAPISemError(info,
"%s should be an object or type name" % source)
- permissive = allow_dict in info.pragma.member_name_exceptions
+ permissive = False
+ if isinstance(allow_dict, str):
+ permissive = allow_dict in info.pragma.member_name_exceptions
# value is a dictionary, check that each member is okay
for (key, arg) in value.items():
@@ -202,24 +396,50 @@ def check_type(value, info, source,
check_type(arg['type'], info, key_source, allow_array=True)
-def check_features(features, info):
+def check_features(features: Optional[object],
+ info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate the ``features`` member.
+
+ ``features`` may be a ``list`` of either ``str`` or ``dict``.
+ Any ``str`` element will be normalized to ``{'name': element}``.
+
+ :forms:
+ :sugared: ``List[Union[str, Feature]]``
+ :canonical: ``List[Feature]``
+
+ :param features: The features member value to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: When ``features`` fails validation.
+ :return: None, ``features`` is normalized in-place as needed.
+ """
if features is None:
return
if not isinstance(features, list):
raise QAPISemError(info, "'features' must be an array")
features[:] = [f if isinstance(f, dict) else {'name': f}
for f in features]
- for f in features:
+ for feat in features:
source = "'features' member"
- assert isinstance(f, dict)
- check_keys(f, info, source, ['name'], ['if'])
- check_name_is_str(f['name'], info, source)
- source = "%s '%s'" % (source, f['name'])
- check_name_lower(f['name'], info, source)
- check_if(f, info, source)
+ assert isinstance(feat, dict)
+ check_keys(feat, info, source, ['name'], ['if'])
+ check_name_is_str(feat['name'], info, source)
+ source = "%s '%s'" % (source, feat['name'])
+ check_name_str(feat['name'], info, source)
+ check_if(feat, info, source)
+
+
+def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as an ``enum`` definition.
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
-def check_enum(expr, info):
+ :raise QAPISemError: When ``expr`` is not a valid ``enum``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
name = expr['enum']
members = expr['data']
prefix = expr.get('prefix')
@@ -241,23 +461,41 @@ def check_enum(expr, info):
source = "%s '%s'" % (source, member_name)
# Enum members may start with a digit
if member_name[0].isdigit():
- member_name = 'd' + member_name # Hack: hide the digit
+ member_name = 'd' + member_name # Hack: hide the digit
check_name_lower(member_name, info, source,
permit_upper=permissive,
permit_underscore=permissive)
check_if(member, info, source)
-def check_struct(expr, info):
- name = expr['struct']
+def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as a ``struct`` definition.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: When ``expr`` is not a valid ``struct``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
+ name = cast(str, expr['struct']) # Checked in check_exprs
members = expr['data']
check_type(members, info, "'data'", allow_dict=name)
check_type(expr.get('base'), info, "'base'")
-def check_union(expr, info):
- name = expr['union']
+def check_union(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as a ``union`` definition.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: when ``expr`` is not a valid ``union``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
+ name = cast(str, expr['union']) # Checked in check_exprs
base = expr.get('base')
discriminator = expr.get('discriminator')
members = expr['data']
@@ -271,6 +509,9 @@ def check_union(expr, info):
raise QAPISemError(info, "'discriminator' requires 'base'")
check_name_is_str(discriminator, info, "'discriminator'")
+ if not isinstance(members, dict):
+ raise QAPISemError(info, "'data' must be an object")
+
for (key, value) in members.items():
source = "'data' member '%s'" % key
if discriminator is None:
@@ -281,11 +522,24 @@ def check_union(expr, info):
check_type(value['type'], info, source, allow_array=not base)
-def check_alternate(expr, info):
+def check_alternate(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as an ``alternate`` definition.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: When ``expr`` is not a valid ``alternate``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
members = expr['data']
if not members:
raise QAPISemError(info, "'data' must not be empty")
+
+ if not isinstance(members, dict):
+ raise QAPISemError(info, "'data' must be an object")
+
for (key, value) in members.items():
source = "'data' member '%s'" % key
check_name_lower(key, info, source)
@@ -294,7 +548,16 @@ def check_alternate(expr, info):
check_type(value['type'], info, source)
-def check_command(expr, info):
+def check_command(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as a ``command`` definition.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: When ``expr`` is not a valid ``command``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
args = expr.get('data')
rets = expr.get('returns')
boxed = expr.get('boxed', False)
@@ -305,7 +568,16 @@ def check_command(expr, info):
check_type(rets, info, "'returns'", allow_array=True)
-def check_event(expr, info):
+def check_event(expr: _JSONObject, info: QAPISourceInfo) -> None:
+ """
+ Normalize and validate this expression as an ``event`` definition.
+
+ :param expr: The expression to validate.
+ :param info: QAPI schema source file information.
+
+ :raise QAPISemError: When ``expr`` is not a valid ``event``.
+ :return: None, ``expr`` is normalized in-place as needed.
+ """
args = expr.get('data')
boxed = expr.get('boxed', False)
@@ -314,11 +586,33 @@ def check_event(expr, info):
check_type(args, info, "'data'", allow_dict=not boxed)
-def check_exprs(exprs):
+def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]:
+ """
+ Validate and normalize a list of parsed QAPI schema expressions.
+
+ This function accepts a list of expressions and metadata as returned
+ by the parser. It destructively normalizes the expressions in-place.
+
+ :param exprs: The list of expressions to normalize and validate.
+
+ :raise QAPISemError: When any expression fails validation.
+ :return: The same list of expressions (now modified).
+ """
for expr_elem in exprs:
- expr = expr_elem['expr']
- info = expr_elem['info']
- doc = expr_elem.get('doc')
+ # Expression
+ assert isinstance(expr_elem['expr'], dict)
+ for key in expr_elem['expr'].keys():
+ assert isinstance(key, str)
+ expr: _JSONObject = expr_elem['expr']
+
+ # QAPISourceInfo
+ assert isinstance(expr_elem['info'], QAPISourceInfo)
+ info: QAPISourceInfo = expr_elem['info']
+
+ # Optional[QAPIDoc]
+ tmp = expr_elem.get('doc')
+ assert tmp is None or isinstance(tmp, QAPIDoc)
+ doc: Optional[QAPIDoc] = tmp
if 'include' in expr:
continue
@@ -338,8 +632,8 @@ def check_exprs(exprs):
else:
raise QAPISemError(info, "expression is missing metatype")
- name = expr[meta]
- check_name_is_str(name, info, "'%s'" % meta)
+ check_name_is_str(expr[meta], info, "'%s'" % meta)
+ name = cast(str, expr[meta])
info.set_defn(meta, name)
check_defn_name_str(name, info, meta)
diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini
index 0a000d58b3..54ca4483d6 100644
--- a/scripts/qapi/mypy.ini
+++ b/scripts/qapi/mypy.ini
@@ -3,16 +3,6 @@ strict = True
disallow_untyped_calls = False
python_version = 3.6
-[mypy-qapi.error]
-disallow_untyped_defs = False
-disallow_incomplete_defs = False
-check_untyped_defs = False
-
-[mypy-qapi.expr]
-disallow_untyped_defs = False
-disallow_incomplete_defs = False
-check_untyped_defs = False
-
[mypy-qapi.parser]
disallow_untyped_defs = False
disallow_incomplete_defs = False
diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index 58267c3db9..ca5e8e18e0 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -18,10 +18,22 @@ from collections import OrderedDict
import os
import re
-from .error import QAPIParseError, QAPISemError
+from .error import QAPISemError, QAPISourceError
from .source import QAPISourceInfo
+class QAPIParseError(QAPISourceError):
+ """Error class for all QAPI schema parsing errors."""
+ def __init__(self, parser, msg):
+ col = 1
+ for ch in parser.src[parser.line_pos:parser.pos]:
+ if ch == '\t':
+ col = (col + 7) % 8 + 1
+ else:
+ col += 1
+ super().__init__(parser.info, msg, col)
+
+
class QAPISchemaParser:
def __init__(self, fname, previously_included=None, incl_info=None):
diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc
index b9e077a164..88efbf71cb 100644
--- a/scripts/qapi/pylintrc
+++ b/scripts/qapi/pylintrc
@@ -2,9 +2,7 @@
# Add files or directories matching the regex patterns to the ignore list.
# The regex matches against base names, not paths.
-ignore-patterns=error.py,
- expr.py,
- parser.py,
+ignore-patterns=parser.py,
schema.py,
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 703b446fd2..3a4172fb74 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -20,7 +20,7 @@ import re
from typing import Optional
from .common import POINTER_SUFFIX, c_name
-from .error import QAPIError, QAPISemError
+from .error import QAPISemError, QAPISourceError
from .expr import check_exprs
from .parser import QAPISchemaParser
@@ -875,7 +875,7 @@ class QAPISchema:
other_ent = self._entity_dict.get(ent.name)
if other_ent:
if other_ent.info:
- where = QAPIError(other_ent.info, None, "previous definition")
+ where = QAPISourceError(other_ent.info, "previous definition")
raise QAPISemError(
ent.info,
"'%s' is already defined\n%s" % (ent.name, where))
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 193a49ec7f..616b393253 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -225,6 +225,11 @@ typedef struct ARMPACKey {
} ARMPACKey;
#endif
+/* See the commentary above the TBFLAG field definitions. */
+typedef struct CPUARMTBFlags {
+ uint32_t flags;
+ target_ulong flags2;
+} CPUARMTBFlags;
typedef struct CPUARMState {
/* Regs for current mode. */
@@ -253,7 +258,7 @@ typedef struct CPUARMState {
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
/* Cached TBFLAGS state. See below for which bits are included. */
- uint32_t hflags;
+ CPUARMTBFlags hflags;
/* Frequently accessed CPSR bits are stored separately for efficiency.
This contains all the other bits. Use cpsr_{read,write} to access
@@ -3377,74 +3382,82 @@ typedef ARMCPU ArchCPU;
#include "exec/cpu-all.h"
/*
- * Bit usage in the TB flags field: bit 31 indicates whether we are
- * in 32 or 64 bit mode. The meaning of the other bits depends on that.
- * We put flags which are shared between 32 and 64 bit mode at the top
- * of the word, and flags which apply to only one mode at the bottom.
+ * We have more than 32-bits worth of state per TB, so we split the data
+ * between tb->flags and tb->cs_base, which is otherwise unused for ARM.
+ * We collect these two parts in CPUARMTBFlags where they are named
+ * flags and flags2 respectively.
*
- * 31 20 18 14 9 0
- * +--------------+-----+-----+----------+--------------+
- * | | | TBFLAG_A32 | |
- * | | +-----+----------+ TBFLAG_AM32 |
- * | TBFLAG_ANY | |TBFLAG_M32| |
- * | +-----------+----------+--------------|
- * | | TBFLAG_A64 |
- * +--------------+-------------------------------------+
- * 31 20 0
+ * The flags that are shared between all execution modes, TBFLAG_ANY,
+ * are stored in flags. The flags that are specific to a given mode
+ * are stores in flags2. Since cs_base is sized on the configured
+ * address size, flags2 always has 64-bits for A64, and a minimum of
+ * 32-bits for A32 and M32.
+ *
+ * The bits for 32-bit A-profile and M-profile partially overlap:
+ *
+ * 31 23 11 10 0
+ * +-------------+----------+----------------+
+ * | | | TBFLAG_A32 |
+ * | TBFLAG_AM32 | +-----+----------+
+ * | | |TBFLAG_M32|
+ * +-------------+----------------+----------+
+ * 31 23 5 4 0
*
* Unless otherwise noted, these bits are cached in env->hflags.
*/
-FIELD(TBFLAG_ANY, AARCH64_STATE, 31, 1)
-FIELD(TBFLAG_ANY, SS_ACTIVE, 30, 1)
-FIELD(TBFLAG_ANY, PSTATE_SS, 29, 1) /* Not cached. */
-FIELD(TBFLAG_ANY, BE_DATA, 28, 1)
-FIELD(TBFLAG_ANY, MMUIDX, 24, 4)
+FIELD(TBFLAG_ANY, AARCH64_STATE, 0, 1)
+FIELD(TBFLAG_ANY, SS_ACTIVE, 1, 1)
+FIELD(TBFLAG_ANY, PSTATE__SS, 2, 1) /* Not cached. */
+FIELD(TBFLAG_ANY, BE_DATA, 3, 1)
+FIELD(TBFLAG_ANY, MMUIDX, 4, 4)
/* Target EL if we take a floating-point-disabled exception */
-FIELD(TBFLAG_ANY, FPEXC_EL, 22, 2)
+FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2)
/* For A-profile only, target EL for debug exceptions. */
-FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 20, 2)
+FIELD(TBFLAG_ANY, DEBUG_TARGET_EL, 10, 2)
+/* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */
+FIELD(TBFLAG_ANY, ALIGN_MEM, 12, 1)
/*
* Bit usage when in AArch32 state, both A- and M-profile.
*/
-FIELD(TBFLAG_AM32, CONDEXEC, 0, 8) /* Not cached. */
-FIELD(TBFLAG_AM32, THUMB, 8, 1) /* Not cached. */
+FIELD(TBFLAG_AM32, CONDEXEC, 24, 8) /* Not cached. */
+FIELD(TBFLAG_AM32, THUMB, 23, 1) /* Not cached. */
/*
* Bit usage when in AArch32 state, for A-profile only.
*/
-FIELD(TBFLAG_A32, VECLEN, 9, 3) /* Not cached. */
-FIELD(TBFLAG_A32, VECSTRIDE, 12, 2) /* Not cached. */
+FIELD(TBFLAG_A32, VECLEN, 0, 3) /* Not cached. */
+FIELD(TBFLAG_A32, VECSTRIDE, 3, 2) /* Not cached. */
/*
* We store the bottom two bits of the CPAR as TB flags and handle
* checks on the other bits at runtime. This shares the same bits as
* VECSTRIDE, which is OK as no XScale CPU has VFP.
* Not cached, because VECLEN+VECSTRIDE are not cached.
*/
-FIELD(TBFLAG_A32, XSCALE_CPAR, 12, 2)
-FIELD(TBFLAG_A32, VFPEN, 14, 1) /* Partially cached, minus FPEXC. */
-FIELD(TBFLAG_A32, SCTLR_B, 15, 1)
-FIELD(TBFLAG_A32, HSTR_ACTIVE, 16, 1)
+FIELD(TBFLAG_A32, XSCALE_CPAR, 5, 2)
+FIELD(TBFLAG_A32, VFPEN, 7, 1) /* Partially cached, minus FPEXC. */
+FIELD(TBFLAG_A32, SCTLR__B, 8, 1) /* Cannot overlap with SCTLR_B */
+FIELD(TBFLAG_A32, HSTR_ACTIVE, 9, 1)
/*
* Indicates whether cp register reads and writes by guest code should access
* the secure or nonsecure bank of banked registers; note that this is not
* the same thing as the current security state of the processor!
*/
-FIELD(TBFLAG_A32, NS, 17, 1)
+FIELD(TBFLAG_A32, NS, 10, 1)
/*
* Bit usage when in AArch32 state, for M-profile only.
*/
/* Handler (ie not Thread) mode */
-FIELD(TBFLAG_M32, HANDLER, 9, 1)
+FIELD(TBFLAG_M32, HANDLER, 0, 1)
/* Whether we should generate stack-limit checks */
-FIELD(TBFLAG_M32, STACKCHECK, 10, 1)
+FIELD(TBFLAG_M32, STACKCHECK, 1, 1)
/* Set if FPCCR.LSPACT is set */
-FIELD(TBFLAG_M32, LSPACT, 11, 1) /* Not cached. */
+FIELD(TBFLAG_M32, LSPACT, 2, 1) /* Not cached. */
/* Set if we must create a new FP context */
-FIELD(TBFLAG_M32, NEW_FP_CTXT_NEEDED, 12, 1) /* Not cached. */
+FIELD(TBFLAG_M32, NEW_FP_CTXT_NEEDED, 3, 1) /* Not cached. */
/* Set if FPCCR.S does not match current security state */
-FIELD(TBFLAG_M32, FPCCR_S_WRONG, 13, 1) /* Not cached. */
+FIELD(TBFLAG_M32, FPCCR_S_WRONG, 4, 1) /* Not cached. */
/*
* Bit usage when in AArch64 state
@@ -3462,6 +3475,26 @@ FIELD(TBFLAG_A64, TCMA, 16, 2)
FIELD(TBFLAG_A64, MTE_ACTIVE, 18, 1)
FIELD(TBFLAG_A64, MTE0_ACTIVE, 19, 1)
+/*
+ * Helpers for using the above.
+ */
+#define DP_TBFLAG_ANY(DST, WHICH, VAL) \
+ (DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL))
+#define DP_TBFLAG_A64(DST, WHICH, VAL) \
+ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A64, WHICH, VAL))
+#define DP_TBFLAG_A32(DST, WHICH, VAL) \
+ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL))
+#define DP_TBFLAG_M32(DST, WHICH, VAL) \
+ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_M32, WHICH, VAL))
+#define DP_TBFLAG_AM32(DST, WHICH, VAL) \
+ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_AM32, WHICH, VAL))
+
+#define EX_TBFLAG_ANY(IN, WHICH) FIELD_EX32(IN.flags, TBFLAG_ANY, WHICH)
+#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A64, WHICH)
+#define EX_TBFLAG_A32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A32, WHICH)
+#define EX_TBFLAG_M32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_M32, WHICH)
+#define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH)
+
/**
* cpu_mmu_index:
* @env: The cpu environment
@@ -3472,7 +3505,7 @@ FIELD(TBFLAG_A64, MTE0_ACTIVE, 19, 1)
*/
static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
{
- return FIELD_EX32(env->hflags, TBFLAG_ANY, MMUIDX);
+ return EX_TBFLAG_ANY(env->hflags, MMUIDX);
}
static inline bool bswap_code(bool sctlr_b)
diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c
index 061c8ff846..9cc3b066e2 100644
--- a/target/arm/helper-a64.c
+++ b/target/arm/helper-a64.c
@@ -1020,7 +1020,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
* the hflags rebuild, since we can pull the composite TBII field
* from there.
*/
- tbii = FIELD_EX32(env->hflags, TBFLAG_A64, TBII);
+ tbii = EX_TBFLAG_A64(env->hflags, TBII);
if ((tbii >> extract64(new_pc, 55, 1)) & 1) {
/* TBI is enabled. */
int core_mmu_idx = cpu_mmu_index(env, false);
diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h
index c139fa81f9..7b706571bb 100644
--- a/target/arm/helper-a64.h
+++ b/target/arm/helper-a64.h
@@ -104,8 +104,7 @@ DEF_HELPER_FLAGS_3(autdb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_2(xpaci, TCG_CALL_NO_RWG_SE, i64, env, i64)
DEF_HELPER_FLAGS_2(xpacd, TCG_CALL_NO_RWG_SE, i64, env, i64)
-DEF_HELPER_FLAGS_3(mte_check1, TCG_CALL_NO_WG, i64, env, i32, i64)
-DEF_HELPER_FLAGS_3(mte_checkN, TCG_CALL_NO_WG, i64, env, i32, i64)
+DEF_HELPER_FLAGS_3(mte_check, TCG_CALL_NO_WG, i64, env, i32, i64)
DEF_HELPER_FLAGS_3(mte_check_zva, TCG_CALL_NO_WG, i64, env, i32, i64)
DEF_HELPER_FLAGS_3(irg, TCG_CALL_NO_RWG, i64, env, i64, i64)
DEF_HELPER_FLAGS_4(addsubg, TCG_CALL_NO_RWG_SE, i64, env, i64, s32, i32)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index d9220be7c5..9b1b98705f 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -12984,42 +12984,49 @@ ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env)
}
#endif
-static uint32_t rebuild_hflags_common(CPUARMState *env, int fp_el,
- ARMMMUIdx mmu_idx, uint32_t flags)
+static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el,
+ ARMMMUIdx mmu_idx,
+ CPUARMTBFlags flags)
{
- flags = FIELD_DP32(flags, TBFLAG_ANY, FPEXC_EL, fp_el);
- flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX,
- arm_to_core_mmu_idx(mmu_idx));
+ DP_TBFLAG_ANY(flags, FPEXC_EL, fp_el);
+ DP_TBFLAG_ANY(flags, MMUIDX, arm_to_core_mmu_idx(mmu_idx));
if (arm_singlestep_active(env)) {
- flags = FIELD_DP32(flags, TBFLAG_ANY, SS_ACTIVE, 1);
+ DP_TBFLAG_ANY(flags, SS_ACTIVE, 1);
}
return flags;
}
-static uint32_t rebuild_hflags_common_32(CPUARMState *env, int fp_el,
- ARMMMUIdx mmu_idx, uint32_t flags)
+static CPUARMTBFlags rebuild_hflags_common_32(CPUARMState *env, int fp_el,
+ ARMMMUIdx mmu_idx,
+ CPUARMTBFlags flags)
{
bool sctlr_b = arm_sctlr_b(env);
if (sctlr_b) {
- flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, 1);
+ DP_TBFLAG_A32(flags, SCTLR__B, 1);
}
if (arm_cpu_data_is_big_endian_a32(env, sctlr_b)) {
- flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1);
+ DP_TBFLAG_ANY(flags, BE_DATA, 1);
}
- flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env));
+ DP_TBFLAG_A32(flags, NS, !access_secure_reg(env));
return rebuild_hflags_common(env, fp_el, mmu_idx, flags);
}
-static uint32_t rebuild_hflags_m32(CPUARMState *env, int fp_el,
- ARMMMUIdx mmu_idx)
+static CPUARMTBFlags rebuild_hflags_m32(CPUARMState *env, int fp_el,
+ ARMMMUIdx mmu_idx)
{
- uint32_t flags = 0;
+ CPUARMTBFlags flags = {};
+ uint32_t ccr = env->v7m.ccr[env->v7m.secure];
+
+ /* Without HaveMainExt, CCR.UNALIGN_TRP is RES1. */
+ if (ccr & R_V7M_CCR_UNALIGN_TRP_MASK) {
+ DP_TBFLAG_ANY(flags, ALIGN_MEM, 1);
+ }
if (arm_v7m_is_handler_mode(env)) {
- flags = FIELD_DP32(flags, TBFLAG_M32, HANDLER, 1);
+ DP_TBFLAG_M32(flags, HANDLER, 1);
}
/*
@@ -13029,56 +13036,60 @@ static uint32_t rebuild_hflags_m32(CPUARMState *env, int fp_el,
*/
if (arm_feature(env, ARM_FEATURE_V8) &&
!((mmu_idx & ARM_MMU_IDX_M_NEGPRI) &&
- (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKOFHFNMIGN_MASK))) {
- flags = FIELD_DP32(flags, TBFLAG_M32, STACKCHECK, 1);
+ (ccr & R_V7M_CCR_STKOFHFNMIGN_MASK))) {
+ DP_TBFLAG_M32(flags, STACKCHECK, 1);
}
return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
}
-static uint32_t rebuild_hflags_aprofile(CPUARMState *env)
+static CPUARMTBFlags rebuild_hflags_aprofile(CPUARMState *env)
{
- int flags = 0;
+ CPUARMTBFlags flags = {};
- flags = FIELD_DP32(flags, TBFLAG_ANY, DEBUG_TARGET_EL,
- arm_debug_target_el(env));
+ DP_TBFLAG_ANY(flags, DEBUG_TARGET_EL, arm_debug_target_el(env));
return flags;
}
-static uint32_t rebuild_hflags_a32(CPUARMState *env, int fp_el,
- ARMMMUIdx mmu_idx)
+static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el,
+ ARMMMUIdx mmu_idx)
{
- uint32_t flags = rebuild_hflags_aprofile(env);
+ CPUARMTBFlags flags = rebuild_hflags_aprofile(env);
+ int el = arm_current_el(env);
+
+ if (arm_sctlr(env, el) & SCTLR_A) {
+ DP_TBFLAG_ANY(flags, ALIGN_MEM, 1);
+ }
if (arm_el_is_aa64(env, 1)) {
- flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
+ DP_TBFLAG_A32(flags, VFPEN, 1);
}
- if (arm_current_el(env) < 2 && env->cp15.hstr_el2 &&
+ if (el < 2 && env->cp15.hstr_el2 &&
(arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) {
- flags = FIELD_DP32(flags, TBFLAG_A32, HSTR_ACTIVE, 1);
+ DP_TBFLAG_A32(flags, HSTR_ACTIVE, 1);
}
return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags);
}
-static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
- ARMMMUIdx mmu_idx)
+static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
+ ARMMMUIdx mmu_idx)
{
- uint32_t flags = rebuild_hflags_aprofile(env);
+ CPUARMTBFlags flags = rebuild_hflags_aprofile(env);
ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr;
uint64_t sctlr;
int tbii, tbid;
- flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1);
+ DP_TBFLAG_ANY(flags, AARCH64_STATE, 1);
/* Get control bits for tagged addresses. */
tbid = aa64_va_parameter_tbi(tcr, mmu_idx);
tbii = tbid & ~aa64_va_parameter_tbid(tcr, mmu_idx);
- flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii);
- flags = FIELD_DP32(flags, TBFLAG_A64, TBID, tbid);
+ DP_TBFLAG_A64(flags, TBII, tbii);
+ DP_TBFLAG_A64(flags, TBID, tbid);
if (cpu_isar_feature(aa64_sve, env_archcpu(env))) {
int sve_el = sve_exception_el(env, el);
@@ -13093,14 +13104,18 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
} else {
zcr_len = sve_zcr_len_for_el(env, el);
}
- flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el);
- flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len);
+ DP_TBFLAG_A64(flags, SVEEXC_EL, sve_el);
+ DP_TBFLAG_A64(flags, ZCR_LEN, zcr_len);
}
sctlr = regime_sctlr(env, stage1);
+ if (sctlr & SCTLR_A) {
+ DP_TBFLAG_ANY(flags, ALIGN_MEM, 1);
+ }
+
if (arm_cpu_data_is_big_endian_a64(el, sctlr)) {
- flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1);
+ DP_TBFLAG_ANY(flags, BE_DATA, 1);
}
if (cpu_isar_feature(aa64_pauth, env_archcpu(env))) {
@@ -13111,14 +13126,14 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
* The decision of which action to take is left to a helper.
*/
if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) {
- flags = FIELD_DP32(flags, TBFLAG_A64, PAUTH_ACTIVE, 1);
+ DP_TBFLAG_A64(flags, PAUTH_ACTIVE, 1);
}
}
if (cpu_isar_feature(aa64_bti, env_archcpu(env))) {
/* Note that SCTLR_EL[23].BT == SCTLR_BT1. */
if (sctlr & (el == 0 ? SCTLR_BT0 : SCTLR_BT1)) {
- flags = FIELD_DP32(flags, TBFLAG_A64, BT, 1);
+ DP_TBFLAG_A64(flags, BT, 1);
}
}
@@ -13130,7 +13145,7 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
case ARMMMUIdx_SE10_1:
case ARMMMUIdx_SE10_1_PAN:
/* TODO: ARMv8.3-NV */
- flags = FIELD_DP32(flags, TBFLAG_A64, UNPRIV, 1);
+ DP_TBFLAG_A64(flags, UNPRIV, 1);
break;
case ARMMMUIdx_E20_2:
case ARMMMUIdx_E20_2_PAN:
@@ -13141,7 +13156,7 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
* gated by HCR_EL2.<E2H,TGE> == '11', and so is LDTR.
*/
if (env->cp15.hcr_el2 & HCR_TGE) {
- flags = FIELD_DP32(flags, TBFLAG_A64, UNPRIV, 1);
+ DP_TBFLAG_A64(flags, UNPRIV, 1);
}
break;
default:
@@ -13159,30 +13174,29 @@ static uint32_t rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
* 4) If no Allocation Tag Access, then all accesses are Unchecked.
*/
if (allocation_tag_access_enabled(env, el, sctlr)) {
- flags = FIELD_DP32(flags, TBFLAG_A64, ATA, 1);
+ DP_TBFLAG_A64(flags, ATA, 1);
if (tbid
&& !(env->pstate & PSTATE_TCO)
&& (sctlr & (el == 0 ? SCTLR_TCF0 : SCTLR_TCF))) {
- flags = FIELD_DP32(flags, TBFLAG_A64, MTE_ACTIVE, 1);
+ DP_TBFLAG_A64(flags, MTE_ACTIVE, 1);
}
}
/* And again for unprivileged accesses, if required. */
- if (FIELD_EX32(flags, TBFLAG_A64, UNPRIV)
+ if (EX_TBFLAG_A64(flags, UNPRIV)
&& tbid
&& !(env->pstate & PSTATE_TCO)
&& (sctlr & SCTLR_TCF0)
&& allocation_tag_access_enabled(env, 0, sctlr)) {
- flags = FIELD_DP32(flags, TBFLAG_A64, MTE0_ACTIVE, 1);
+ DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1);
}
/* Cache TCMA as well as TBI. */
- flags = FIELD_DP32(flags, TBFLAG_A64, TCMA,
- aa64_va_parameter_tcma(tcr, mmu_idx));
+ DP_TBFLAG_A64(flags, TCMA, aa64_va_parameter_tcma(tcr, mmu_idx));
}
return rebuild_hflags_common(env, fp_el, mmu_idx, flags);
}
-static uint32_t rebuild_hflags_internal(CPUARMState *env)
+static CPUARMTBFlags rebuild_hflags_internal(CPUARMState *env)
{
int el = arm_current_el(env);
int fp_el = fp_exception_el(env, el);
@@ -13211,6 +13225,7 @@ void HELPER(rebuild_hflags_m32_newel)(CPUARMState *env)
int el = arm_current_el(env);
int fp_el = fp_exception_el(env, el);
ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el);
+
env->hflags = rebuild_hflags_m32(env, fp_el, mmu_idx);
}
@@ -13253,12 +13268,14 @@ void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el)
static inline void assert_hflags_rebuild_correctly(CPUARMState *env)
{
#ifdef CONFIG_DEBUG_TCG
- uint32_t env_flags_current = env->hflags;
- uint32_t env_flags_rebuilt = rebuild_hflags_internal(env);
-
- if (unlikely(env_flags_current != env_flags_rebuilt)) {
- fprintf(stderr, "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n",
- env_flags_current, env_flags_rebuilt);
+ CPUARMTBFlags c = env->hflags;
+ CPUARMTBFlags r = rebuild_hflags_internal(env);
+
+ if (unlikely(c.flags != r.flags || c.flags2 != r.flags2)) {
+ fprintf(stderr, "TCG hflags mismatch "
+ "(current:(0x%08x,0x" TARGET_FMT_lx ")"
+ " rebuilt:(0x%08x,0x" TARGET_FMT_lx ")\n",
+ c.flags, c.flags2, r.flags, r.flags2);
abort();
}
#endif
@@ -13267,15 +13284,15 @@ static inline void assert_hflags_rebuild_correctly(CPUARMState *env)
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags)
{
- uint32_t flags = env->hflags;
+ CPUARMTBFlags flags;
- *cs_base = 0;
assert_hflags_rebuild_correctly(env);
+ flags = env->hflags;
- if (FIELD_EX32(flags, TBFLAG_ANY, AARCH64_STATE)) {
+ if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) {
*pc = env->pc;
if (cpu_isar_feature(aa64_bti, env_archcpu(env))) {
- flags = FIELD_DP32(flags, TBFLAG_A64, BTYPE, env->btype);
+ DP_TBFLAG_A64(flags, BTYPE, env->btype);
}
} else {
*pc = env->regs[15];
@@ -13284,7 +13301,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
if (arm_feature(env, ARM_FEATURE_M_SECURITY) &&
FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S)
!= env->v7m.secure) {
- flags = FIELD_DP32(flags, TBFLAG_M32, FPCCR_S_WRONG, 1);
+ DP_TBFLAG_M32(flags, FPCCR_S_WRONG, 1);
}
if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) &&
@@ -13296,12 +13313,12 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
* active FP context; we must create a new FP context before
* executing any FP insn.
*/
- flags = FIELD_DP32(flags, TBFLAG_M32, NEW_FP_CTXT_NEEDED, 1);
+ DP_TBFLAG_M32(flags, NEW_FP_CTXT_NEEDED, 1);
}
bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK;
if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) {
- flags = FIELD_DP32(flags, TBFLAG_M32, LSPACT, 1);
+ DP_TBFLAG_M32(flags, LSPACT, 1);
}
} else {
/*
@@ -13309,21 +13326,18 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
* Note that VECLEN+VECSTRIDE are RES0 for M-profile.
*/
if (arm_feature(env, ARM_FEATURE_XSCALE)) {
- flags = FIELD_DP32(flags, TBFLAG_A32,
- XSCALE_CPAR, env->cp15.c15_cpar);
+ DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar);
} else {
- flags = FIELD_DP32(flags, TBFLAG_A32, VECLEN,
- env->vfp.vec_len);
- flags = FIELD_DP32(flags, TBFLAG_A32, VECSTRIDE,
- env->vfp.vec_stride);
+ DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len);
+ DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride);
}
if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) {
- flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1);
+ DP_TBFLAG_A32(flags, VFPEN, 1);
}
}
- flags = FIELD_DP32(flags, TBFLAG_AM32, THUMB, env->thumb);
- flags = FIELD_DP32(flags, TBFLAG_AM32, CONDEXEC, env->condexec_bits);
+ DP_TBFLAG_AM32(flags, THUMB, env->thumb);
+ DP_TBFLAG_AM32(flags, CONDEXEC, env->condexec_bits);
}
/*
@@ -13333,14 +13347,14 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
* 0 x Inactive (the TB flag for SS is always 0)
* 1 0 Active-pending
* 1 1 Active-not-pending
- * SS_ACTIVE is set in hflags; PSTATE_SS is computed every TB.
+ * SS_ACTIVE is set in hflags; PSTATE__SS is computed every TB.
*/
- if (FIELD_EX32(flags, TBFLAG_ANY, SS_ACTIVE) &&
- (env->pstate & PSTATE_SS)) {
- flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1);
+ if (EX_TBFLAG_ANY(flags, SS_ACTIVE) && (env->pstate & PSTATE_SS)) {
+ DP_TBFLAG_ANY(flags, PSTATE__SS, 1);
}
- *pflags = flags;
+ *pflags = flags.flags;
+ *cs_base = flags.flags2;
}
#ifdef TARGET_AARCH64
diff --git a/target/arm/internals.h b/target/arm/internals.h
index f11bd32696..886db56b58 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -26,6 +26,7 @@
#define TARGET_ARM_INTERNALS_H
#include "hw/registerfields.h"
+#include "tcg/tcg-gvec-desc.h"
#include "syndrome.h"
/* register banks for CPU modes */
@@ -1142,14 +1143,10 @@ FIELD(MTEDESC, MIDX, 0, 4)
FIELD(MTEDESC, TBI, 4, 2)
FIELD(MTEDESC, TCMA, 6, 2)
FIELD(MTEDESC, WRITE, 8, 1)
-FIELD(MTEDESC, ESIZE, 9, 5)
-FIELD(MTEDESC, TSIZE, 14, 10) /* mte_checkN only */
-
-bool mte_probe1(CPUARMState *env, uint32_t desc, uint64_t ptr);
-uint64_t mte_check1(CPUARMState *env, uint32_t desc,
- uint64_t ptr, uintptr_t ra);
-uint64_t mte_checkN(CPUARMState *env, uint32_t desc,
- uint64_t ptr, uintptr_t ra);
+FIELD(MTEDESC, SIZEM1, 9, SIMD_DATA_BITS - 9) /* size - 1 */
+
+bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr);
+uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra);
static inline int allocation_tag_from_addr(uint64_t ptr)
{
diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c
index 8be17e1b70..a6fccc6e69 100644
--- a/target/arm/mte_helper.c
+++ b/target/arm/mte_helper.c
@@ -121,7 +121,7 @@ static uint8_t *allocation_tag_mem(CPUARMState *env, int ptr_mmu_idx,
* exception for inaccessible pages, and resolves the virtual address
* into the softmmu tlb.
*
- * When RA == 0, this is for mte_probe1. The page is expected to be
+ * When RA == 0, this is for mte_probe. The page is expected to be
* valid. Indicate to probe_access_flags no-fault, then assert that
* we received a valid page.
*/
@@ -617,80 +617,6 @@ static void mte_check_fail(CPUARMState *env, uint32_t desc,
}
}
-/*
- * Perform an MTE checked access for a single logical or atomic access.
- */
-static bool mte_probe1_int(CPUARMState *env, uint32_t desc, uint64_t ptr,
- uintptr_t ra, int bit55)
-{
- int mem_tag, mmu_idx, ptr_tag, size;
- MMUAccessType type;
- uint8_t *mem;
-
- ptr_tag = allocation_tag_from_addr(ptr);
-
- if (tcma_check(desc, bit55, ptr_tag)) {
- return true;
- }
-
- mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
- type = FIELD_EX32(desc, MTEDESC, WRITE) ? MMU_DATA_STORE : MMU_DATA_LOAD;
- size = FIELD_EX32(desc, MTEDESC, ESIZE);
-
- mem = allocation_tag_mem(env, mmu_idx, ptr, type, size,
- MMU_DATA_LOAD, 1, ra);
- if (!mem) {
- return true;
- }
-
- mem_tag = load_tag1(ptr, mem);
- return ptr_tag == mem_tag;
-}
-
-/*
- * No-fault version of mte_check1, to be used by SVE for MemSingleNF.
- * Returns false if the access is Checked and the check failed. This
- * is only intended to probe the tag -- the validity of the page must
- * be checked beforehand.
- */
-bool mte_probe1(CPUARMState *env, uint32_t desc, uint64_t ptr)
-{
- int bit55 = extract64(ptr, 55, 1);
-
- /* If TBI is disabled, the access is unchecked. */
- if (unlikely(!tbi_check(desc, bit55))) {
- return true;
- }
-
- return mte_probe1_int(env, desc, ptr, 0, bit55);
-}
-
-uint64_t mte_check1(CPUARMState *env, uint32_t desc,
- uint64_t ptr, uintptr_t ra)
-{
- int bit55 = extract64(ptr, 55, 1);
-
- /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
- if (unlikely(!tbi_check(desc, bit55))) {
- return ptr;
- }
-
- if (unlikely(!mte_probe1_int(env, desc, ptr, ra, bit55))) {
- mte_check_fail(env, desc, ptr, ra);
- }
-
- return useronly_clean_ptr(ptr);
-}
-
-uint64_t HELPER(mte_check1)(CPUARMState *env, uint32_t desc, uint64_t ptr)
-{
- return mte_check1(env, desc, ptr, GETPC());
-}
-
-/*
- * Perform an MTE checked access for multiple logical accesses.
- */
-
/**
* checkN:
* @tag: tag memory to test
@@ -753,59 +679,70 @@ static int checkN(uint8_t *mem, int odd, int cmp, int count)
return n;
}
-uint64_t mte_checkN(CPUARMState *env, uint32_t desc,
- uint64_t ptr, uintptr_t ra)
+/**
+ * mte_probe_int() - helper for mte_probe and mte_check
+ * @env: CPU environment
+ * @desc: MTEDESC descriptor
+ * @ptr: virtual address of the base of the access
+ * @fault: return virtual address of the first check failure
+ *
+ * Internal routine for both mte_probe and mte_check.
+ * Return zero on failure, filling in *fault.
+ * Return negative on trivial success for tbi disabled.
+ * Return positive on success with tbi enabled.
+ */
+static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr,
+ uintptr_t ra, uint64_t *fault)
{
int mmu_idx, ptr_tag, bit55;
- uint64_t ptr_last, ptr_end, prev_page, next_page;
- uint64_t tag_first, tag_end;
- uint64_t tag_byte_first, tag_byte_end;
- uint32_t esize, total, tag_count, tag_size, n, c;
+ uint64_t ptr_last, prev_page, next_page;
+ uint64_t tag_first, tag_last;
+ uint64_t tag_byte_first, tag_byte_last;
+ uint32_t sizem1, tag_count, tag_size, n, c;
uint8_t *mem1, *mem2;
MMUAccessType type;
bit55 = extract64(ptr, 55, 1);
+ *fault = ptr;
/* If TBI is disabled, the access is unchecked, and ptr is not dirty. */
if (unlikely(!tbi_check(desc, bit55))) {
- return ptr;
+ return -1;
}
ptr_tag = allocation_tag_from_addr(ptr);
if (tcma_check(desc, bit55, ptr_tag)) {
- goto done;
+ return 1;
}
mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX);
type = FIELD_EX32(desc, MTEDESC, WRITE) ? MMU_DATA_STORE : MMU_DATA_LOAD;
- esize = FIELD_EX32(desc, MTEDESC, ESIZE);
- total = FIELD_EX32(desc, MTEDESC, TSIZE);
+ sizem1 = FIELD_EX32(desc, MTEDESC, SIZEM1);
- /* Find the addr of the end of the access, and of the last element. */
- ptr_end = ptr + total;
- ptr_last = ptr_end - esize;
+ /* Find the addr of the end of the access */
+ ptr_last = ptr + sizem1;
/* Round the bounds to the tag granule, and compute the number of tags. */
tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE);
- tag_end = QEMU_ALIGN_UP(ptr_last, TAG_GRANULE);
- tag_count = (tag_end - tag_first) / TAG_GRANULE;
+ tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE);
+ tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1;
/* Round the bounds to twice the tag granule, and compute the bytes. */
tag_byte_first = QEMU_ALIGN_DOWN(ptr, 2 * TAG_GRANULE);
- tag_byte_end = QEMU_ALIGN_UP(ptr_last, 2 * TAG_GRANULE);
+ tag_byte_last = QEMU_ALIGN_DOWN(ptr_last, 2 * TAG_GRANULE);
/* Locate the page boundaries. */
prev_page = ptr & TARGET_PAGE_MASK;
next_page = prev_page + TARGET_PAGE_SIZE;
- if (likely(tag_end - prev_page <= TARGET_PAGE_SIZE)) {
+ if (likely(tag_last - prev_page <= TARGET_PAGE_SIZE)) {
/* Memory access stays on one page. */
- tag_size = (tag_byte_end - tag_byte_first) / (2 * TAG_GRANULE);
- mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, total,
+ tag_size = ((tag_byte_last - tag_byte_first) / (2 * TAG_GRANULE)) + 1;
+ mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1,
MMU_DATA_LOAD, tag_size, ra);
if (!mem1) {
- goto done;
+ return 1;
}
/* Perform all of the comparisons. */
n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count);
@@ -815,9 +752,9 @@ uint64_t mte_checkN(CPUARMState *env, uint32_t desc,
mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr,
MMU_DATA_LOAD, tag_size, ra);
- tag_size = (tag_byte_end - next_page) / (2 * TAG_GRANULE);
+ tag_size = ((tag_byte_last - next_page) / (2 * TAG_GRANULE)) + 1;
mem2 = allocation_tag_mem(env, mmu_idx, next_page, type,
- ptr_end - next_page,
+ ptr_last - next_page + 1,
MMU_DATA_LOAD, tag_size, ra);
/*
@@ -831,31 +768,57 @@ uint64_t mte_checkN(CPUARMState *env, uint32_t desc,
}
if (n == c) {
if (!mem2) {
- goto done;
+ return 1;
}
n += checkN(mem2, 0, ptr_tag, tag_count - c);
}
}
+ if (likely(n == tag_count)) {
+ return 1;
+ }
+
/*
- * If we failed, we know which granule. Compute the element that
- * is first in that granule, and signal failure on that element.
+ * If we failed, we know which granule. For the first granule, the
+ * failure address is @ptr, the first byte accessed. Otherwise the
+ * failure address is the first byte of the nth granule.
*/
- if (unlikely(n < tag_count)) {
- uint64_t fail_ofs;
-
- fail_ofs = tag_first + n * TAG_GRANULE - ptr;
- fail_ofs = ROUND_UP(fail_ofs, esize);
- mte_check_fail(env, desc, ptr + fail_ofs, ra);
+ if (n > 0) {
+ *fault = tag_first + n * TAG_GRANULE;
}
+ return 0;
+}
- done:
+uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra)
+{
+ uint64_t fault;
+ int ret = mte_probe_int(env, desc, ptr, ra, &fault);
+
+ if (unlikely(ret == 0)) {
+ mte_check_fail(env, desc, fault, ra);
+ } else if (ret < 0) {
+ return ptr;
+ }
return useronly_clean_ptr(ptr);
}
-uint64_t HELPER(mte_checkN)(CPUARMState *env, uint32_t desc, uint64_t ptr)
+uint64_t HELPER(mte_check)(CPUARMState *env, uint32_t desc, uint64_t ptr)
{
- return mte_checkN(env, desc, ptr, GETPC());
+ return mte_check(env, desc, ptr, GETPC());
+}
+
+/*
+ * No-fault version of mte_check, to be used by SVE for MemSingleNF.
+ * Returns false if the access is Checked and the check failed. This
+ * is only intended to probe the tag -- the validity of the page must
+ * be checked beforehand.
+ */
+bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr)
+{
+ uint64_t fault;
+ int ret = mte_probe_int(env, desc, ptr, 0, &fault);
+
+ return ret != 0;
}
/*
diff --git a/target/arm/neon-ls.decode b/target/arm/neon-ls.decode
index c17f5019e3..0a2a0e15db 100644
--- a/target/arm/neon-ls.decode
+++ b/target/arm/neon-ls.decode
@@ -46,7 +46,7 @@ VLD_all_lanes 1111 0100 1 . 1 0 rn:4 .... 11 n:2 size:2 t:1 a:1 rm:4 \
VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 00 n:2 reg_idx:3 align:1 rm:4 \
vd=%vd_dp size=0 stride=1
-VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 01 n:2 reg_idx:2 align:2 rm:4 \
+VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 01 n:2 reg_idx:2 . align:1 rm:4 \
vd=%vd_dp size=1 stride=%imm1_5_p1
-VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 10 n:2 reg_idx:1 align:3 rm:4 \
+VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 10 n:2 reg_idx:1 . align:2 rm:4 \
vd=%vd_dp size=2 stride=%imm1_6_p1
diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c
index fd6c58f96a..c068dfa0d5 100644
--- a/target/arm/sve_helper.c
+++ b/target/arm/sve_helper.c
@@ -4382,13 +4382,9 @@ static void sve_cont_ldst_watchpoints(SVEContLdSt *info, CPUARMState *env,
#endif
}
-typedef uint64_t mte_check_fn(CPUARMState *, uint32_t, uint64_t, uintptr_t);
-
-static inline QEMU_ALWAYS_INLINE
-void sve_cont_ldst_mte_check_int(SVEContLdSt *info, CPUARMState *env,
- uint64_t *vg, target_ulong addr, int esize,
- int msize, uint32_t mtedesc, uintptr_t ra,
- mte_check_fn *check)
+static void sve_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env,
+ uint64_t *vg, target_ulong addr, int esize,
+ int msize, uint32_t mtedesc, uintptr_t ra)
{
intptr_t mem_off, reg_off, reg_last;
@@ -4405,7 +4401,7 @@ void sve_cont_ldst_mte_check_int(SVEContLdSt *info, CPUARMState *env,
uint64_t pg = vg[reg_off >> 6];
do {
if ((pg >> (reg_off & 63)) & 1) {
- check(env, mtedesc, addr, ra);
+ mte_check(env, mtedesc, addr, ra);
}
reg_off += esize;
mem_off += msize;
@@ -4422,7 +4418,7 @@ void sve_cont_ldst_mte_check_int(SVEContLdSt *info, CPUARMState *env,
uint64_t pg = vg[reg_off >> 6];
do {
if ((pg >> (reg_off & 63)) & 1) {
- check(env, mtedesc, addr, ra);
+ mte_check(env, mtedesc, addr, ra);
}
reg_off += esize;
mem_off += msize;
@@ -4431,30 +4427,6 @@ void sve_cont_ldst_mte_check_int(SVEContLdSt *info, CPUARMState *env,
}
}
-typedef void sve_cont_ldst_mte_check_fn(SVEContLdSt *info, CPUARMState *env,
- uint64_t *vg, target_ulong addr,
- int esize, int msize, uint32_t mtedesc,
- uintptr_t ra);
-
-static void sve_cont_ldst_mte_check1(SVEContLdSt *info, CPUARMState *env,
- uint64_t *vg, target_ulong addr,
- int esize, int msize, uint32_t mtedesc,
- uintptr_t ra)
-{
- sve_cont_ldst_mte_check_int(info, env, vg, addr, esize, msize,
- mtedesc, ra, mte_check1);
-}
-
-static void sve_cont_ldst_mte_checkN(SVEContLdSt *info, CPUARMState *env,
- uint64_t *vg, target_ulong addr,
- int esize, int msize, uint32_t mtedesc,
- uintptr_t ra)
-{
- sve_cont_ldst_mte_check_int(info, env, vg, addr, esize, msize,
- mtedesc, ra, mte_checkN);
-}
-
-
/*
* Common helper for all contiguous 1,2,3,4-register predicated stores.
*/
@@ -4463,8 +4435,7 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr,
uint32_t desc, const uintptr_t retaddr,
const int esz, const int msz, const int N, uint32_t mtedesc,
sve_ldst1_host_fn *host_fn,
- sve_ldst1_tlb_fn *tlb_fn,
- sve_cont_ldst_mte_check_fn *mte_check_fn)
+ sve_ldst1_tlb_fn *tlb_fn)
{
const unsigned rd = simd_data(desc);
const intptr_t reg_max = simd_oprsz(desc);
@@ -4493,9 +4464,9 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr,
* Handle mte checks for all active elements.
* Since TBI must be set for MTE, !mtedesc => !mte_active.
*/
- if (mte_check_fn && mtedesc) {
- mte_check_fn(&info, env, vg, addr, 1 << esz, N << msz,
- mtedesc, retaddr);
+ if (mtedesc) {
+ sve_cont_ldst_mte_check(&info, env, vg, addr, 1 << esz, N << msz,
+ mtedesc, retaddr);
}
flags = info.page[0].flags | info.page[1].flags;
@@ -4621,8 +4592,7 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr,
mtedesc = 0;
}
- sve_ldN_r(env, vg, addr, desc, ra, esz, msz, N, mtedesc, host_fn, tlb_fn,
- N == 1 ? sve_cont_ldst_mte_check1 : sve_cont_ldst_mte_checkN);
+ sve_ldN_r(env, vg, addr, desc, ra, esz, msz, N, mtedesc, host_fn, tlb_fn);
}
#define DO_LD1_1(NAME, ESZ) \
@@ -4630,7 +4600,7 @@ void HELPER(sve_##NAME##_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, 1, 0, \
- sve_##NAME##_host, sve_##NAME##_tlb, NULL); \
+ sve_##NAME##_host, sve_##NAME##_tlb); \
} \
void HELPER(sve_##NAME##_r_mte)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
@@ -4644,22 +4614,22 @@ void HELPER(sve_##NAME##_le_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \
- sve_##NAME##_le_host, sve_##NAME##_le_tlb, NULL); \
+ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \
} \
void HELPER(sve_##NAME##_be_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \
- sve_##NAME##_be_host, sve_##NAME##_be_tlb, NULL); \
+ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \
} \
void HELPER(sve_##NAME##_le_r_mte)(CPUARMState *env, void *vg, \
- target_ulong addr, uint32_t desc) \
+ target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \
sve_##NAME##_le_host, sve_##NAME##_le_tlb); \
} \
void HELPER(sve_##NAME##_be_r_mte)(CPUARMState *env, void *vg, \
- target_ulong addr, uint32_t desc) \
+ target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \
sve_##NAME##_be_host, sve_##NAME##_be_tlb); \
@@ -4693,7 +4663,7 @@ void HELPER(sve_ld##N##bb_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), MO_8, MO_8, N, 0, \
- sve_ld1bb_host, sve_ld1bb_tlb, NULL); \
+ sve_ld1bb_host, sve_ld1bb_tlb); \
} \
void HELPER(sve_ld##N##bb_r_mte)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
@@ -4707,13 +4677,13 @@ void HELPER(sve_ld##N##SUFF##_le_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \
- sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb, NULL); \
+ sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb); \
} \
void HELPER(sve_ld##N##SUFF##_be_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \
- sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb, NULL); \
+ sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb); \
} \
void HELPER(sve_ld##N##SUFF##_le_r_mte)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
@@ -4826,7 +4796,7 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr,
if (fault == FAULT_FIRST) {
/* Trapping mte check for the first-fault element. */
if (mtedesc) {
- mte_check1(env, mtedesc, addr + mem_off, retaddr);
+ mte_check(env, mtedesc, addr + mem_off, retaddr);
}
/*
@@ -4869,7 +4839,7 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr,
/* Watchpoint hit, see below. */
goto do_fault;
}
- if (mtedesc && !mte_probe1(env, mtedesc, addr + mem_off)) {
+ if (mtedesc && !mte_probe(env, mtedesc, addr + mem_off)) {
goto do_fault;
}
/*
@@ -4919,7 +4889,7 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr,
& BP_MEM_READ)) {
goto do_fault;
}
- if (mtedesc && !mte_probe1(env, mtedesc, addr + mem_off)) {
+ if (mtedesc && !mte_probe(env, mtedesc, addr + mem_off)) {
goto do_fault;
}
host_fn(vd, reg_off, host + mem_off);
@@ -5090,8 +5060,7 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr,
uint32_t desc, const uintptr_t retaddr,
const int esz, const int msz, const int N, uint32_t mtedesc,
sve_ldst1_host_fn *host_fn,
- sve_ldst1_tlb_fn *tlb_fn,
- sve_cont_ldst_mte_check_fn *mte_check_fn)
+ sve_ldst1_tlb_fn *tlb_fn)
{
const unsigned rd = simd_data(desc);
const intptr_t reg_max = simd_oprsz(desc);
@@ -5117,9 +5086,9 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr,
* Handle mte checks for all active elements.
* Since TBI must be set for MTE, !mtedesc => !mte_active.
*/
- if (mte_check_fn && mtedesc) {
- mte_check_fn(&info, env, vg, addr, 1 << esz, N << msz,
- mtedesc, retaddr);
+ if (mtedesc) {
+ sve_cont_ldst_mte_check(&info, env, vg, addr, 1 << esz, N << msz,
+ mtedesc, retaddr);
}
flags = info.page[0].flags | info.page[1].flags;
@@ -5233,8 +5202,7 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr,
mtedesc = 0;
}
- sve_stN_r(env, vg, addr, desc, ra, esz, msz, N, mtedesc, host_fn, tlb_fn,
- N == 1 ? sve_cont_ldst_mte_check1 : sve_cont_ldst_mte_checkN);
+ sve_stN_r(env, vg, addr, desc, ra, esz, msz, N, mtedesc, host_fn, tlb_fn);
}
#define DO_STN_1(N, NAME, ESZ) \
@@ -5242,7 +5210,7 @@ void HELPER(sve_st##N##NAME##_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, N, 0, \
- sve_st1##NAME##_host, sve_st1##NAME##_tlb, NULL); \
+ sve_st1##NAME##_host, sve_st1##NAME##_tlb); \
} \
void HELPER(sve_st##N##NAME##_r_mte)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
@@ -5256,13 +5224,13 @@ void HELPER(sve_st##N##NAME##_le_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \
- sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb, NULL); \
+ sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb); \
} \
void HELPER(sve_st##N##NAME##_be_r)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
{ \
sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \
- sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb, NULL); \
+ sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb); \
} \
void HELPER(sve_st##N##NAME##_le_r_mte)(CPUARMState *env, void *vg, \
target_ulong addr, uint32_t desc) \
@@ -5373,7 +5341,7 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
info.attrs, BP_MEM_READ, retaddr);
}
if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) {
- mte_check1(env, mtedesc, addr, retaddr);
+ mte_check(env, mtedesc, addr, retaddr);
}
host_fn(&scratch, reg_off, info.host);
} else {
@@ -5386,7 +5354,7 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
BP_MEM_READ, retaddr);
}
if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) {
- mte_check1(env, mtedesc, addr, retaddr);
+ mte_check(env, mtedesc, addr, retaddr);
}
tlb_fn(env, &scratch, reg_off, addr, retaddr);
}
@@ -5552,7 +5520,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
*/
addr = base + (off_fn(vm, reg_off) << scale);
if (mtedesc) {
- mte_check1(env, mtedesc, addr, retaddr);
+ mte_check(env, mtedesc, addr, retaddr);
}
tlb_fn(env, vd, reg_off, addr, retaddr);
@@ -5588,7 +5556,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
}
if (mtedesc &&
arm_tlb_mte_tagged(&info.attrs) &&
- !mte_probe1(env, mtedesc, addr)) {
+ !mte_probe(env, mtedesc, addr)) {
goto fault;
}
@@ -5773,7 +5741,7 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
}
if (mtedesc && arm_tlb_mte_tagged(&info.attrs)) {
- mte_check1(env, mtedesc, addr, retaddr);
+ mte_check(env, mtedesc, addr, retaddr);
}
}
i += 1;
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 0b42e53500..95897e63af 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -272,11 +272,11 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr,
desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid);
desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma);
desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write);
- desc = FIELD_DP32(desc, MTEDESC, ESIZE, 1 << log2_size);
+ desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << log2_size) - 1);
tcg_desc = tcg_const_i32(desc);
ret = new_tmp_a64(s);
- gen_helper_mte_check1(ret, cpu_env, tcg_desc, addr);
+ gen_helper_mte_check(ret, cpu_env, tcg_desc, addr);
tcg_temp_free_i32(tcg_desc);
return ret;
@@ -295,9 +295,9 @@ TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write,
* For MTE, check multiple logical sequential accesses.
*/
TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write,
- bool tag_checked, int log2_esize, int total_size)
+ bool tag_checked, int size)
{
- if (tag_checked && s->mte_active[0] && total_size != (1 << log2_esize)) {
+ if (tag_checked && s->mte_active[0]) {
TCGv_i32 tcg_desc;
TCGv_i64 ret;
int desc = 0;
@@ -306,17 +306,16 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write,
desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid);
desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma);
desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write);
- desc = FIELD_DP32(desc, MTEDESC, ESIZE, 1 << log2_esize);
- desc = FIELD_DP32(desc, MTEDESC, TSIZE, total_size);
+ desc = FIELD_DP32(desc, MTEDESC, SIZEM1, size - 1);
tcg_desc = tcg_const_i32(desc);
ret = new_tmp_a64(s);
- gen_helper_mte_checkN(ret, cpu_env, tcg_desc, addr);
+ gen_helper_mte_check(ret, cpu_env, tcg_desc, addr);
tcg_temp_free_i32(tcg_desc);
return ret;
}
- return gen_mte_check1(s, addr, is_write, tag_checked, log2_esize);
+ return clean_data_tbi(s, addr);
}
typedef struct DisasCompare64 {
@@ -887,19 +886,19 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
* Store from GPR register to memory.
*/
static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source,
- TCGv_i64 tcg_addr, int size, int memidx,
+ TCGv_i64 tcg_addr, MemOp memop, int memidx,
bool iss_valid,
unsigned int iss_srt,
bool iss_sf, bool iss_ar)
{
- g_assert(size <= 3);
- tcg_gen_qemu_st_i64(source, tcg_addr, memidx, s->be_data + size);
+ memop = finalize_memop(s, memop);
+ tcg_gen_qemu_st_i64(source, tcg_addr, memidx, memop);
if (iss_valid) {
uint32_t syn;
syn = syn_data_abort_with_iss(0,
- size,
+ (memop & MO_SIZE),
false,
iss_srt,
iss_sf,
@@ -910,37 +909,28 @@ static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source,
}
static void do_gpr_st(DisasContext *s, TCGv_i64 source,
- TCGv_i64 tcg_addr, int size,
+ TCGv_i64 tcg_addr, MemOp memop,
bool iss_valid,
unsigned int iss_srt,
bool iss_sf, bool iss_ar)
{
- do_gpr_st_memidx(s, source, tcg_addr, size, get_mem_index(s),
+ do_gpr_st_memidx(s, source, tcg_addr, memop, get_mem_index(s),
iss_valid, iss_srt, iss_sf, iss_ar);
}
/*
* Load from memory to GPR register
*/
-static void do_gpr_ld_memidx(DisasContext *s,
- TCGv_i64 dest, TCGv_i64 tcg_addr,
- int size, bool is_signed,
- bool extend, int memidx,
+static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
+ MemOp memop, bool extend, int memidx,
bool iss_valid, unsigned int iss_srt,
bool iss_sf, bool iss_ar)
{
- MemOp memop = s->be_data + size;
-
- g_assert(size <= 3);
-
- if (is_signed) {
- memop += MO_SIGN;
- }
-
+ memop = finalize_memop(s, memop);
tcg_gen_qemu_ld_i64(dest, tcg_addr, memidx, memop);
- if (extend && is_signed) {
- g_assert(size < 3);
+ if (extend && (memop & MO_SIGN)) {
+ g_assert((memop & MO_SIZE) <= MO_32);
tcg_gen_ext32u_i64(dest, dest);
}
@@ -948,8 +938,8 @@ static void do_gpr_ld_memidx(DisasContext *s,
uint32_t syn;
syn = syn_data_abort_with_iss(0,
- size,
- is_signed,
+ (memop & MO_SIZE),
+ (memop & MO_SIGN) != 0,
iss_srt,
iss_sf,
iss_ar,
@@ -958,14 +948,12 @@ static void do_gpr_ld_memidx(DisasContext *s,
}
}
-static void do_gpr_ld(DisasContext *s,
- TCGv_i64 dest, TCGv_i64 tcg_addr,
- int size, bool is_signed, bool extend,
+static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
+ MemOp memop, bool extend,
bool iss_valid, unsigned int iss_srt,
bool iss_sf, bool iss_ar)
{
- do_gpr_ld_memidx(s, dest, tcg_addr, size, is_signed, extend,
- get_mem_index(s),
+ do_gpr_ld_memidx(s, dest, tcg_addr, memop, extend, get_mem_index(s),
iss_valid, iss_srt, iss_sf, iss_ar);
}
@@ -975,25 +963,33 @@ static void do_gpr_ld(DisasContext *s,
static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size)
{
/* This writes the bottom N bits of a 128 bit wide vector to memory */
- TCGv_i64 tmp = tcg_temp_new_i64();
- tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(s, srcidx, MO_64));
+ TCGv_i64 tmplo = tcg_temp_new_i64();
+ MemOp mop;
+
+ tcg_gen_ld_i64(tmplo, cpu_env, fp_reg_offset(s, srcidx, MO_64));
+
if (size < 4) {
- tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s),
- s->be_data + size);
+ mop = finalize_memop(s, size);
+ tcg_gen_qemu_st_i64(tmplo, tcg_addr, get_mem_index(s), mop);
} else {
bool be = s->be_data == MO_BE;
TCGv_i64 tcg_hiaddr = tcg_temp_new_i64();
+ TCGv_i64 tmphi = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmphi, cpu_env, fp_reg_hi_offset(s, srcidx));
+
+ mop = s->be_data | MO_Q;
+ tcg_gen_qemu_st_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s),
+ mop | (s->align_mem ? MO_ALIGN_16 : 0));
tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8);
- tcg_gen_qemu_st_i64(tmp, be ? tcg_hiaddr : tcg_addr, get_mem_index(s),
- s->be_data | MO_Q);
- tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(s, srcidx));
- tcg_gen_qemu_st_i64(tmp, be ? tcg_addr : tcg_hiaddr, get_mem_index(s),
- s->be_data | MO_Q);
+ tcg_gen_qemu_st_i64(be ? tmplo : tmphi, tcg_hiaddr,
+ get_mem_index(s), mop);
+
tcg_temp_free_i64(tcg_hiaddr);
+ tcg_temp_free_i64(tmphi);
}
- tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmplo);
}
/*
@@ -1004,10 +1000,11 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size)
/* This always zero-extends and writes to a full 128 bit wide vector */
TCGv_i64 tmplo = tcg_temp_new_i64();
TCGv_i64 tmphi = NULL;
+ MemOp mop;
if (size < 4) {
- MemOp memop = s->be_data + size;
- tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), memop);
+ mop = finalize_memop(s, size);
+ tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), mop);
} else {
bool be = s->be_data == MO_BE;
TCGv_i64 tcg_hiaddr;
@@ -1015,11 +1012,12 @@ static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size)
tmphi = tcg_temp_new_i64();
tcg_hiaddr = tcg_temp_new_i64();
+ mop = s->be_data | MO_Q;
+ tcg_gen_qemu_ld_i64(be ? tmphi : tmplo, tcg_addr, get_mem_index(s),
+ mop | (s->align_mem ? MO_ALIGN_16 : 0));
tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8);
- tcg_gen_qemu_ld_i64(tmplo, be ? tcg_hiaddr : tcg_addr, get_mem_index(s),
- s->be_data | MO_Q);
- tcg_gen_qemu_ld_i64(tmphi, be ? tcg_addr : tcg_hiaddr, get_mem_index(s),
- s->be_data | MO_Q);
+ tcg_gen_qemu_ld_i64(be ? tmplo : tmphi, tcg_hiaddr,
+ get_mem_index(s), mop);
tcg_temp_free_i64(tcg_hiaddr);
}
@@ -1148,24 +1146,24 @@ static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src,
/* Store from vector register to memory */
static void do_vec_st(DisasContext *s, int srcidx, int element,
- TCGv_i64 tcg_addr, int size, MemOp endian)
+ TCGv_i64 tcg_addr, MemOp mop)
{
TCGv_i64 tcg_tmp = tcg_temp_new_i64();
- read_vec_element(s, tcg_tmp, srcidx, element, size);
- tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), endian | size);
+ read_vec_element(s, tcg_tmp, srcidx, element, mop & MO_SIZE);
+ tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), mop);
tcg_temp_free_i64(tcg_tmp);
}
/* Load from memory to vector register */
static void do_vec_ld(DisasContext *s, int destidx, int element,
- TCGv_i64 tcg_addr, int size, MemOp endian)
+ TCGv_i64 tcg_addr, MemOp mop)
{
TCGv_i64 tcg_tmp = tcg_temp_new_i64();
- tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), endian | size);
- write_vec_element(s, tcg_tmp, destidx, element, size);
+ tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), mop);
+ write_vec_element(s, tcg_tmp, destidx, element, mop & MO_SIZE);
tcg_temp_free_i64(tcg_tmp);
}
@@ -2701,7 +2699,8 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn)
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn),
true, rn != 31, size);
- do_gpr_st(s, cpu_reg(s, rt), clean_addr, size, true, rt,
+ /* TODO: ARMv8.4-LSE SCTLR.nAA */
+ do_gpr_st(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, true, rt,
disas_ldst_compute_iss_sf(size, false, 0), is_lasr);
return;
@@ -2718,8 +2717,9 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn)
}
clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn),
false, rn != 31, size);
- do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false, false, true, rt,
- disas_ldst_compute_iss_sf(size, false, 0), is_lasr);
+ /* TODO: ARMv8.4-LSE SCTLR.nAA */
+ do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size | MO_ALIGN, false, true,
+ rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr);
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
return;
@@ -2831,8 +2831,8 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn)
/* Only unsigned 32bit loads target 32bit registers. */
bool iss_sf = opc != 0;
- do_gpr_ld(s, tcg_rt, clean_addr, size, is_signed, false,
- true, rt, iss_sf, false);
+ do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN,
+ false, true, rt, iss_sf, false);
}
tcg_temp_free_i64(clean_addr);
}
@@ -2966,8 +2966,7 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
}
clean_addr = gen_mte_checkN(s, dirty_addr, !is_load,
- (wback || rn != 31) && !set_tag,
- size, 2 << size);
+ (wback || rn != 31) && !set_tag, 2 << size);
if (is_vector) {
if (is_load) {
@@ -2991,11 +2990,11 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
/* Do not modify tcg_rt before recognizing any exception
* from the second load.
*/
- do_gpr_ld(s, tmp, clean_addr, size, is_signed, false,
- false, 0, false, false);
+ do_gpr_ld(s, tmp, clean_addr, size + is_signed * MO_SIGN,
+ false, false, 0, false, false);
tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size);
- do_gpr_ld(s, tcg_rt2, clean_addr, size, is_signed, false,
- false, 0, false, false);
+ do_gpr_ld(s, tcg_rt2, clean_addr, size + is_signed * MO_SIGN,
+ false, false, 0, false, false);
tcg_gen_mov_i64(tcg_rt, tmp);
tcg_temp_free_i64(tmp);
@@ -3126,8 +3125,8 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
do_gpr_st_memidx(s, tcg_rt, clean_addr, size, memidx,
iss_valid, rt, iss_sf, false);
} else {
- do_gpr_ld_memidx(s, tcg_rt, clean_addr, size,
- is_signed, is_extended, memidx,
+ do_gpr_ld_memidx(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN,
+ is_extended, memidx,
iss_valid, rt, iss_sf, false);
}
}
@@ -3231,9 +3230,8 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn,
do_gpr_st(s, tcg_rt, clean_addr, size,
true, rt, iss_sf, false);
} else {
- do_gpr_ld(s, tcg_rt, clean_addr, size,
- is_signed, is_extended,
- true, rt, iss_sf, false);
+ do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN,
+ is_extended, true, rt, iss_sf, false);
}
}
}
@@ -3316,8 +3314,8 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn,
do_gpr_st(s, tcg_rt, clean_addr, size,
true, rt, iss_sf, false);
} else {
- do_gpr_ld(s, tcg_rt, clean_addr, size, is_signed, is_extended,
- true, rt, iss_sf, false);
+ do_gpr_ld(s, tcg_rt, clean_addr, size + is_signed * MO_SIGN,
+ is_extended, true, rt, iss_sf, false);
}
}
}
@@ -3404,7 +3402,7 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn,
* full load-acquire (we only need "load-acquire processor consistent"),
* but we choose to implement them as full LDAQ.
*/
- do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false, false,
+ do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false,
true, rt, disas_ldst_compute_iss_sf(size, false, 0), true);
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
return;
@@ -3477,7 +3475,7 @@ static void disas_ldst_pac(DisasContext *s, uint32_t insn,
is_wback || rn != 31, size);
tcg_rt = cpu_reg(s, rt);
- do_gpr_ld(s, tcg_rt, clean_addr, size, /* is_signed */ false,
+ do_gpr_ld(s, tcg_rt, clean_addr, size,
/* extend */ false, /* iss_valid */ !is_wback,
/* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false);
@@ -3509,15 +3507,18 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn)
int size = extract32(insn, 30, 2);
TCGv_i64 clean_addr, dirty_addr;
bool is_store = false;
- bool is_signed = false;
bool extend = false;
bool iss_sf;
+ MemOp mop;
if (!dc_isar_feature(aa64_rcpc_8_4, s)) {
unallocated_encoding(s);
return;
}
+ /* TODO: ARMv8.4-LSE SCTLR.nAA */
+ mop = size | MO_ALIGN;
+
switch (opc) {
case 0: /* STLURB */
is_store = true;
@@ -3529,21 +3530,21 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn)
unallocated_encoding(s);
return;
}
- is_signed = true;
+ mop |= MO_SIGN;
break;
case 3: /* LDAPURS* 32-bit variant */
if (size > 1) {
unallocated_encoding(s);
return;
}
- is_signed = true;
+ mop |= MO_SIGN;
extend = true; /* zero-extend 32->64 after signed load */
break;
default:
g_assert_not_reached();
}
- iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc);
+ iss_sf = disas_ldst_compute_iss_sf(size, (mop & MO_SIGN) != 0, opc);
if (rn == 31) {
gen_check_sp_alignment(s);
@@ -3556,14 +3557,14 @@ static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn)
if (is_store) {
/* Store-Release semantics */
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
- do_gpr_st(s, cpu_reg(s, rt), clean_addr, size, true, rt, iss_sf, true);
+ do_gpr_st(s, cpu_reg(s, rt), clean_addr, mop, true, rt, iss_sf, true);
} else {
/*
* Load-AcquirePC semantics; we implement as the slightly more
* restrictive Load-Acquire.
*/
- do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, is_signed, extend,
- true, rt, iss_sf, true);
+ do_gpr_ld(s, cpu_reg(s, rt), clean_addr, mop,
+ extend, true, rt, iss_sf, true);
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
}
}
@@ -3634,7 +3635,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
bool is_postidx = extract32(insn, 23, 1);
bool is_q = extract32(insn, 30, 1);
TCGv_i64 clean_addr, tcg_rn, tcg_ebytes;
- MemOp endian = s->be_data;
+ MemOp endian, align, mop;
int total; /* total bytes */
int elements; /* elements per vector */
@@ -3702,6 +3703,7 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
}
/* For our purposes, bytes are always little-endian. */
+ endian = s->be_data;
if (size == 0) {
endian = MO_LE;
}
@@ -3714,17 +3716,23 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
* promote consecutive little-endian elements below.
*/
clean_addr = gen_mte_checkN(s, tcg_rn, is_store, is_postidx || rn != 31,
- size, total);
+ total);
/*
* Consecutive little-endian elements from a single register
* can be promoted to a larger little-endian operation.
*/
+ align = MO_ALIGN;
if (selem == 1 && endian == MO_LE) {
+ align = pow2_align(size);
size = 3;
}
- elements = (is_q ? 16 : 8) >> size;
+ if (!s->align_mem) {
+ align = 0;
+ }
+ mop = endian | size | align;
+ elements = (is_q ? 16 : 8) >> size;
tcg_ebytes = tcg_const_i64(1 << size);
for (r = 0; r < rpt; r++) {
int e;
@@ -3733,9 +3741,9 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
for (xs = 0; xs < selem; xs++) {
int tt = (rt + r + xs) % 32;
if (is_store) {
- do_vec_st(s, tt, e, clean_addr, size, endian);
+ do_vec_st(s, tt, e, clean_addr, mop);
} else {
- do_vec_ld(s, tt, e, clean_addr, size, endian);
+ do_vec_ld(s, tt, e, clean_addr, mop);
}
tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes);
}
@@ -3807,6 +3815,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
int index = is_q << 3 | S << 2 | size;
int xs, total;
TCGv_i64 clean_addr, tcg_rn, tcg_ebytes;
+ MemOp mop;
if (extract32(insn, 31, 1)) {
unallocated_encoding(s);
@@ -3867,7 +3876,8 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
tcg_rn = cpu_reg_sp(s, rn);
clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31,
- scale, total);
+ total);
+ mop = finalize_memop(s, scale);
tcg_ebytes = tcg_const_i64(1 << scale);
for (xs = 0; xs < selem; xs++) {
@@ -3875,8 +3885,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
/* Load and replicate to all elements */
TCGv_i64 tcg_tmp = tcg_temp_new_i64();
- tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr,
- get_mem_index(s), s->be_data + scale);
+ tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), mop);
tcg_gen_gvec_dup_i64(scale, vec_full_reg_offset(s, rt),
(is_q + 1) * 8, vec_full_reg_size(s),
tcg_tmp);
@@ -3884,9 +3893,9 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
} else {
/* Load/store one element per register */
if (is_load) {
- do_vec_ld(s, rt, index, clean_addr, scale, s->be_data);
+ do_vec_ld(s, rt, index, clean_addr, mop);
} else {
- do_vec_st(s, rt, index, clean_addr, scale, s->be_data);
+ do_vec_st(s, rt, index, clean_addr, mop);
}
}
tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes);
@@ -14672,7 +14681,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUARMState *env = cpu->env_ptr;
ARMCPU *arm_cpu = env_archcpu(env);
- uint32_t tb_flags = dc->base.tb->flags;
+ CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb);
int bound, core_mmu_idx;
dc->isar = &arm_cpu->isar;
@@ -14686,28 +14695,29 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
!arm_el_is_aa64(env, 3);
dc->thumb = 0;
dc->sctlr_b = 0;
- dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE;
+ dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE;
dc->condexec_mask = 0;
dc->condexec_cond = 0;
- core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX);
+ core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX);
dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx);
- dc->tbii = FIELD_EX32(tb_flags, TBFLAG_A64, TBII);
- dc->tbid = FIELD_EX32(tb_flags, TBFLAG_A64, TBID);
- dc->tcma = FIELD_EX32(tb_flags, TBFLAG_A64, TCMA);
+ dc->tbii = EX_TBFLAG_A64(tb_flags, TBII);
+ dc->tbid = EX_TBFLAG_A64(tb_flags, TBID);
+ dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA);
dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx);
#if !defined(CONFIG_USER_ONLY)
dc->user = (dc->current_el == 0);
#endif
- dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL);
- dc->sve_excp_el = FIELD_EX32(tb_flags, TBFLAG_A64, SVEEXC_EL);
- dc->sve_len = (FIELD_EX32(tb_flags, TBFLAG_A64, ZCR_LEN) + 1) * 16;
- dc->pauth_active = FIELD_EX32(tb_flags, TBFLAG_A64, PAUTH_ACTIVE);
- dc->bt = FIELD_EX32(tb_flags, TBFLAG_A64, BT);
- dc->btype = FIELD_EX32(tb_flags, TBFLAG_A64, BTYPE);
- dc->unpriv = FIELD_EX32(tb_flags, TBFLAG_A64, UNPRIV);
- dc->ata = FIELD_EX32(tb_flags, TBFLAG_A64, ATA);
- dc->mte_active[0] = FIELD_EX32(tb_flags, TBFLAG_A64, MTE_ACTIVE);
- dc->mte_active[1] = FIELD_EX32(tb_flags, TBFLAG_A64, MTE0_ACTIVE);
+ dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL);
+ dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM);
+ dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL);
+ dc->sve_len = (EX_TBFLAG_A64(tb_flags, ZCR_LEN) + 1) * 16;
+ dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE);
+ dc->bt = EX_TBFLAG_A64(tb_flags, BT);
+ dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE);
+ dc->unpriv = EX_TBFLAG_A64(tb_flags, UNPRIV);
+ dc->ata = EX_TBFLAG_A64(tb_flags, ATA);
+ dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE);
+ dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE);
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;
@@ -14734,10 +14744,10 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
* emit code to generate a software step exception
* end the TB
*/
- dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE);
- dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS);
+ dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE);
+ dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS);
dc->is_ldex = false;
- dc->debug_target_el = FIELD_EX32(tb_flags, TBFLAG_ANY, DEBUG_TARGET_EL);
+ dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL);
/* Bound the number of insns to execute to those left on the page. */
bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h
index 3668b671dd..868d355048 100644
--- a/target/arm/translate-a64.h
+++ b/target/arm/translate-a64.h
@@ -44,7 +44,7 @@ TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr);
TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write,
bool tag_checked, int log2_size);
TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write,
- bool tag_checked, int count, int log2_esize);
+ bool tag_checked, int size);
/* We should have at some point before trying to access an FP register
* done the necessary access check, so assert that
diff --git a/target/arm/translate-neon.c.inc b/target/arm/translate-neon.c.inc
index f6c68e30ab..a02b8369a1 100644
--- a/target/arm/translate-neon.c.inc
+++ b/target/arm/translate-neon.c.inc
@@ -429,7 +429,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
{
/* Neon load/store multiple structures */
int nregs, interleave, spacing, reg, n;
- MemOp endian = s->be_data;
+ MemOp mop, align, endian;
int mmu_idx = get_mem_index(s);
int size = a->size;
TCGv_i64 tmp64;
@@ -473,20 +473,36 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
}
/* For our purposes, bytes are always little-endian. */
+ endian = s->be_data;
if (size == 0) {
endian = MO_LE;
}
+
+ /* Enforce alignment requested by the instruction */
+ if (a->align) {
+ align = pow2_align(a->align + 2); /* 4 ** a->align */
+ } else {
+ align = s->align_mem ? MO_ALIGN : 0;
+ }
+
/*
* Consecutive little-endian elements from a single register
* can be promoted to a larger little-endian operation.
*/
if (interleave == 1 && endian == MO_LE) {
+ /* Retain any natural alignment. */
+ if (align == MO_ALIGN) {
+ align = pow2_align(size);
+ }
size = 3;
}
+
tmp64 = tcg_temp_new_i64();
addr = tcg_temp_new_i32();
tmp = tcg_const_i32(1 << size);
load_reg_var(s, addr, a->rn);
+
+ mop = endian | size | align;
for (reg = 0; reg < nregs; reg++) {
for (n = 0; n < 8 >> size; n++) {
int xs;
@@ -494,13 +510,16 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
int tt = a->vd + reg + spacing * xs;
if (a->l) {
- gen_aa32_ld_i64(s, tmp64, addr, mmu_idx, endian | size);
+ gen_aa32_ld_internal_i64(s, tmp64, addr, mmu_idx, mop);
neon_store_element64(tt, n, size, tmp64);
} else {
neon_load_element64(tmp64, tt, n, size);
- gen_aa32_st_i64(s, tmp64, addr, mmu_idx, endian | size);
+ gen_aa32_st_internal_i64(s, tmp64, addr, mmu_idx, mop);
}
tcg_gen_add_i32(addr, addr, tmp);
+
+ /* Subsequent memory operations inherit alignment */
+ mop &= ~MO_AMASK;
}
}
}
@@ -520,6 +539,7 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a)
int size = a->size;
int nregs = a->n + 1;
TCGv_i32 addr, tmp;
+ MemOp mop, align;
if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
return false;
@@ -530,18 +550,33 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a)
return false;
}
+ align = 0;
if (size == 3) {
if (nregs != 4 || a->a == 0) {
return false;
}
/* For VLD4 size == 3 a == 1 means 32 bits at 16 byte alignment */
- size = 2;
- }
- if (nregs == 1 && a->a == 1 && size == 0) {
- return false;
- }
- if (nregs == 3 && a->a == 1) {
- return false;
+ size = MO_32;
+ align = MO_ALIGN_16;
+ } else if (a->a) {
+ switch (nregs) {
+ case 1:
+ if (size == 0) {
+ return false;
+ }
+ align = MO_ALIGN;
+ break;
+ case 2:
+ align = pow2_align(size + 1);
+ break;
+ case 3:
+ return false;
+ case 4:
+ align = pow2_align(size + 2);
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
if (!vfp_access_check(s)) {
@@ -554,13 +589,12 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a)
*/
stride = a->t ? 2 : 1;
vec_size = nregs == 1 ? stride * 8 : 8;
-
+ mop = size | align;
tmp = tcg_temp_new_i32();
addr = tcg_temp_new_i32();
load_reg_var(s, addr, a->rn);
for (reg = 0; reg < nregs; reg++) {
- gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s),
- s->be_data | size);
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), mop);
if ((vd & 1) && vec_size == 16) {
/*
* We cannot write 16 bytes at once because the
@@ -576,6 +610,9 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a)
}
tcg_gen_addi_i32(addr, addr, 1 << size);
vd += stride;
+
+ /* Subsequent memory operations inherit alignment */
+ mop &= ~MO_AMASK;
}
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(addr);
@@ -592,6 +629,7 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a)
int nregs = a->n + 1;
int vd = a->vd;
TCGv_i32 addr, tmp;
+ MemOp mop;
if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
return false;
@@ -606,7 +644,7 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a)
switch (nregs) {
case 1:
if (((a->align & (1 << a->size)) != 0) ||
- (a->size == 2 && ((a->align & 3) == 1 || (a->align & 3) == 2))) {
+ (a->size == 2 && (a->align == 1 || a->align == 2))) {
return false;
}
break;
@@ -621,7 +659,7 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a)
}
break;
case 4:
- if ((a->size == 2) && ((a->align & 3) == 3)) {
+ if (a->size == 2 && a->align == 3) {
return false;
}
break;
@@ -641,25 +679,58 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a)
return true;
}
+ /* Pick up SCTLR settings */
+ mop = finalize_memop(s, a->size);
+
+ if (a->align) {
+ MemOp align_op;
+
+ switch (nregs) {
+ case 1:
+ /* For VLD1, use natural alignment. */
+ align_op = MO_ALIGN;
+ break;
+ case 2:
+ /* For VLD2, use double alignment. */
+ align_op = pow2_align(a->size + 1);
+ break;
+ case 4:
+ if (a->size == MO_32) {
+ /*
+ * For VLD4.32, align = 1 is double alignment, align = 2 is
+ * quad alignment; align = 3 is rejected above.
+ */
+ align_op = pow2_align(a->size + a->align);
+ } else {
+ /* For VLD4.8 and VLD.16, we want quad alignment. */
+ align_op = pow2_align(a->size + 2);
+ }
+ break;
+ default:
+ /* For VLD3, the alignment field is zero and rejected above. */
+ g_assert_not_reached();
+ }
+
+ mop = (mop & ~MO_AMASK) | align_op;
+ }
+
tmp = tcg_temp_new_i32();
addr = tcg_temp_new_i32();
load_reg_var(s, addr, a->rn);
- /*
- * TODO: if we implemented alignment exceptions, we should check
- * addr against the alignment encoded in a->align here.
- */
+
for (reg = 0; reg < nregs; reg++) {
if (a->l) {
- gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s),
- s->be_data | a->size);
+ gen_aa32_ld_internal_i32(s, tmp, addr, get_mem_index(s), mop);
neon_store_element(vd, a->reg_idx, a->size, tmp);
} else { /* Store */
neon_load_element(tmp, vd, a->reg_idx, a->size);
- gen_aa32_st_i32(s, tmp, addr, get_mem_index(s),
- s->be_data | a->size);
+ gen_aa32_st_internal_i32(s, tmp, addr, get_mem_index(s), mop);
}
vd += a->stride;
tcg_gen_addi_i32(addr, addr, 1 << a->size);
+
+ /* Subsequent memory operations inherit alignment */
+ mop &= ~MO_AMASK;
}
tcg_temp_free_i32(addr);
tcg_temp_free_i32(tmp);
diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c
index 0eefb61214..864ed669c4 100644
--- a/target/arm/translate-sve.c
+++ b/target/arm/translate-sve.c
@@ -4264,7 +4264,7 @@ static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm)
dirty_addr = tcg_temp_new_i64();
tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm);
- clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8);
+ clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len);
tcg_temp_free_i64(dirty_addr);
/*
@@ -4352,7 +4352,7 @@ static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm)
dirty_addr = tcg_temp_new_i64();
tcg_gen_addi_i64(dirty_addr, cpu_reg_sp(s, rn), imm);
- clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len, MO_8);
+ clean_addr = gen_mte_checkN(s, dirty_addr, false, rn != 31, len);
tcg_temp_free_i64(dirty_addr);
/* Note that unpredicated load/store of vector/predicate registers
@@ -4509,8 +4509,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr,
desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid);
desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma);
desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write);
- desc = FIELD_DP32(desc, MTEDESC, ESIZE, 1 << msz);
- desc = FIELD_DP32(desc, MTEDESC, TSIZE, mte_n << msz);
+ desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (mte_n << msz) - 1);
desc <<= SVE_MTEDESC_SHIFT;
} else {
addr = clean_data_tbi(s, addr);
@@ -5002,7 +5001,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a)
clean_addr = gen_mte_check1(s, temp, false, true, msz);
tcg_gen_qemu_ld_i64(temp, clean_addr, get_mem_index(s),
- s->be_data | dtype_mop[a->dtype]);
+ finalize_memop(s, dtype_mop[a->dtype]));
/* Broadcast to *all* elements. */
tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd),
@@ -5189,7 +5188,7 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm,
desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid);
desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma);
desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write);
- desc = FIELD_DP32(desc, MTEDESC, ESIZE, 1 << msz);
+ desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << msz) - 1);
desc <<= SVE_MTEDESC_SHIFT;
}
desc = simd_desc(vsz, vsz, desc | scale);
diff --git a/target/arm/translate-vfp.c.inc b/target/arm/translate-vfp.c.inc
index 10766f210c..e20d9c7ba6 100644
--- a/target/arm/translate-vfp.c.inc
+++ b/target/arm/translate-vfp.c.inc
@@ -1364,11 +1364,11 @@ static bool trans_VLDR_VSTR_hp(DisasContext *s, arg_VLDR_VSTR_sp *a)
addr = add_reg_for_lit(s, a->rn, offset);
tmp = tcg_temp_new_i32();
if (a->l) {
- gen_aa32_ld16u(s, tmp, addr, get_mem_index(s));
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), MO_UW | MO_ALIGN);
vfp_store_reg32(tmp, a->vd);
} else {
vfp_load_reg32(tmp, a->vd);
- gen_aa32_st16(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UW | MO_ALIGN);
}
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(addr);
@@ -1398,11 +1398,11 @@ static bool trans_VLDR_VSTR_sp(DisasContext *s, arg_VLDR_VSTR_sp *a)
addr = add_reg_for_lit(s, a->rn, offset);
tmp = tcg_temp_new_i32();
if (a->l) {
- gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
vfp_store_reg32(tmp, a->vd);
} else {
vfp_load_reg32(tmp, a->vd);
- gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
}
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(addr);
@@ -1439,11 +1439,11 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a)
addr = add_reg_for_lit(s, a->rn, offset);
tmp = tcg_temp_new_i64();
if (a->l) {
- gen_aa32_ld64(s, tmp, addr, get_mem_index(s));
+ gen_aa32_ld_i64(s, tmp, addr, get_mem_index(s), MO_Q | MO_ALIGN_4);
vfp_store_reg64(tmp, a->vd);
} else {
vfp_load_reg64(tmp, a->vd);
- gen_aa32_st64(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i64(s, tmp, addr, get_mem_index(s), MO_Q | MO_ALIGN_4);
}
tcg_temp_free_i64(tmp);
tcg_temp_free_i32(addr);
@@ -1503,12 +1503,12 @@ static bool trans_VLDM_VSTM_sp(DisasContext *s, arg_VLDM_VSTM_sp *a)
for (i = 0; i < n; i++) {
if (a->l) {
/* load */
- gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
vfp_store_reg32(tmp, a->vd + i);
} else {
/* store */
vfp_load_reg32(tmp, a->vd + i);
- gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
}
tcg_gen_addi_i32(addr, addr, offset);
}
@@ -1586,12 +1586,12 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a)
for (i = 0; i < n; i++) {
if (a->l) {
/* load */
- gen_aa32_ld64(s, tmp, addr, get_mem_index(s));
+ gen_aa32_ld_i64(s, tmp, addr, get_mem_index(s), MO_Q | MO_ALIGN_4);
vfp_store_reg64(tmp, a->vd + i);
} else {
/* store */
vfp_load_reg64(tmp, a->vd + i);
- gen_aa32_st64(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i64(s, tmp, addr, get_mem_index(s), MO_Q | MO_ALIGN_4);
}
tcg_gen_addi_i32(addr, addr, offset);
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 7103da2d7a..43ff0d4b8a 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -908,7 +908,23 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var)
#define IS_USER_ONLY 0
#endif
-/* Abstractions of "generate code to do a guest load/store for
+MemOp pow2_align(unsigned i)
+{
+ static const MemOp mop_align[] = {
+ 0, MO_ALIGN_2, MO_ALIGN_4, MO_ALIGN_8, MO_ALIGN_16,
+ /*
+ * FIXME: TARGET_PAGE_BITS_MIN affects TLB_FLAGS_MASK such
+ * that 256-bit alignment (MO_ALIGN_32) cannot be supported:
+ * see get_alignment_bits(). Enforce only 128-bit alignment for now.
+ */
+ MO_ALIGN_16
+ };
+ g_assert(i < ARRAY_SIZE(mop_align));
+ return mop_align[i];
+}
+
+/*
+ * Abstractions of "generate code to do a guest load/store for
* AArch32", where a vaddr is always 32 bits (and is zero
* extended if we're a 64 bit core) and data is also
* 32 bits unless specifically doing a 64 bit access.
@@ -916,7 +932,7 @@ static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var)
* that the address argument is TCGv_i32 rather than TCGv.
*/
-static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op)
+static TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op)
{
TCGv addr = tcg_temp_new();
tcg_gen_extu_i32_tl(addr, a32);
@@ -928,80 +944,47 @@ static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op)
return addr;
}
-static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
- int index, MemOp opc)
+/*
+ * Internal routines are used for NEON cases where the endianness
+ * and/or alignment has already been taken into account and manipulated.
+ */
+static void gen_aa32_ld_internal_i32(DisasContext *s, TCGv_i32 val,
+ TCGv_i32 a32, int index, MemOp opc)
{
- TCGv addr;
-
- if (arm_dc_feature(s, ARM_FEATURE_M) &&
- !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) {
- opc |= MO_ALIGN;
- }
-
- addr = gen_aa32_addr(s, a32, opc);
+ TCGv addr = gen_aa32_addr(s, a32, opc);
tcg_gen_qemu_ld_i32(val, addr, index, opc);
tcg_temp_free(addr);
}
-static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
- int index, MemOp opc)
+static void gen_aa32_st_internal_i32(DisasContext *s, TCGv_i32 val,
+ TCGv_i32 a32, int index, MemOp opc)
{
- TCGv addr;
-
- if (arm_dc_feature(s, ARM_FEATURE_M) &&
- !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) {
- opc |= MO_ALIGN;
- }
-
- addr = gen_aa32_addr(s, a32, opc);
+ TCGv addr = gen_aa32_addr(s, a32, opc);
tcg_gen_qemu_st_i32(val, addr, index, opc);
tcg_temp_free(addr);
}
-#define DO_GEN_LD(SUFF, OPC) \
-static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \
- TCGv_i32 a32, int index) \
-{ \
- gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \
-}
+static void gen_aa32_ld_internal_i64(DisasContext *s, TCGv_i64 val,
+ TCGv_i32 a32, int index, MemOp opc)
+{
+ TCGv addr = gen_aa32_addr(s, a32, opc);
-#define DO_GEN_ST(SUFF, OPC) \
-static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \
- TCGv_i32 a32, int index) \
-{ \
- gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \
-}
+ tcg_gen_qemu_ld_i64(val, addr, index, opc);
-static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val)
-{
/* Not needed for user-mode BE32, where we use MO_BE instead. */
- if (!IS_USER_ONLY && s->sctlr_b) {
+ if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) {
tcg_gen_rotri_i64(val, val, 32);
}
-}
-
-static void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32,
- int index, MemOp opc)
-{
- TCGv addr = gen_aa32_addr(s, a32, opc);
- tcg_gen_qemu_ld_i64(val, addr, index, opc);
- gen_aa32_frob64(s, val);
tcg_temp_free(addr);
}
-static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val,
- TCGv_i32 a32, int index)
-{
- gen_aa32_ld_i64(s, val, a32, index, MO_Q | s->be_data);
-}
-
-static void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32,
- int index, MemOp opc)
+static void gen_aa32_st_internal_i64(DisasContext *s, TCGv_i64 val,
+ TCGv_i32 a32, int index, MemOp opc)
{
TCGv addr = gen_aa32_addr(s, a32, opc);
/* Not needed for user-mode BE32, where we use MO_BE instead. */
- if (!IS_USER_ONLY && s->sctlr_b) {
+ if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) {
TCGv_i64 tmp = tcg_temp_new_i64();
tcg_gen_rotri_i64(tmp, val, 32);
tcg_gen_qemu_st_i64(tmp, addr, index, opc);
@@ -1012,10 +995,54 @@ static void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32,
tcg_temp_free(addr);
}
+static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
+ int index, MemOp opc)
+{
+ gen_aa32_ld_internal_i32(s, val, a32, index, finalize_memop(s, opc));
+}
+
+static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32,
+ int index, MemOp opc)
+{
+ gen_aa32_st_internal_i32(s, val, a32, index, finalize_memop(s, opc));
+}
+
+static void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32,
+ int index, MemOp opc)
+{
+ gen_aa32_ld_internal_i64(s, val, a32, index, finalize_memop(s, opc));
+}
+
+static void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32,
+ int index, MemOp opc)
+{
+ gen_aa32_st_internal_i64(s, val, a32, index, finalize_memop(s, opc));
+}
+
+#define DO_GEN_LD(SUFF, OPC) \
+ static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \
+ TCGv_i32 a32, int index) \
+ { \
+ gen_aa32_ld_i32(s, val, a32, index, OPC); \
+ }
+
+#define DO_GEN_ST(SUFF, OPC) \
+ static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \
+ TCGv_i32 a32, int index) \
+ { \
+ gen_aa32_st_i32(s, val, a32, index, OPC); \
+ }
+
+static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val,
+ TCGv_i32 a32, int index)
+{
+ gen_aa32_ld_i64(s, val, a32, index, MO_Q);
+}
+
static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val,
TCGv_i32 a32, int index)
{
- gen_aa32_st_i64(s, val, a32, index, MO_Q | s->be_data);
+ gen_aa32_st_i64(s, val, a32, index, MO_Q);
}
DO_GEN_LD(8u, MO_UB)
@@ -4984,16 +5011,13 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
TCGv_i32 tmp2 = tcg_temp_new_i32();
TCGv_i64 t64 = tcg_temp_new_i64();
- /* For AArch32, architecturally the 32-bit word at the lowest
+ /*
+ * For AArch32, architecturally the 32-bit word at the lowest
* address is always Rt and the one at addr+4 is Rt2, even if
* the CPU is big-endian. That means we don't want to do a
- * gen_aa32_ld_i64(), which invokes gen_aa32_frob64() as if
- * for an architecturally 64-bit access, but instead do a
- * 64-bit access using MO_BE if appropriate and then split
- * the two halves.
- * This only makes a difference for BE32 user-mode, where
- * frob64() must not flip the two halves of the 64-bit data
- * but this code must treat BE32 user-mode like BE32 system.
+ * gen_aa32_ld_i64(), which checks SCTLR_B as if for an
+ * architecturally 64-bit access, but instead do a 64-bit access
+ * using MO_BE if appropriate and then split the two halves.
*/
TCGv taddr = gen_aa32_addr(s, addr, opc);
@@ -5053,14 +5077,15 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
TCGv_i64 n64 = tcg_temp_new_i64();
t2 = load_reg(s, rt2);
- /* For AArch32, architecturally the 32-bit word at the lowest
+
+ /*
+ * For AArch32, architecturally the 32-bit word at the lowest
* address is always Rt and the one at addr+4 is Rt2, even if
* the CPU is big-endian. Since we're going to treat this as a
* single 64-bit BE store, we need to put the two halves in the
* opposite order for BE to LE, so that they end up in the right
- * places.
- * We don't want gen_aa32_frob64() because that does the wrong
- * thing for BE32 usermode.
+ * places. We don't want gen_aa32_st_i64, because that checks
+ * SCTLR_B as if for an architectural 64-bit access.
*/
if (s->be_data == MO_BE) {
tcg_gen_concat_i32_i64(n64, t2, t1);
@@ -5190,11 +5215,11 @@ static void gen_srs(DisasContext *s,
}
tcg_gen_addi_i32(addr, addr, offset);
tmp = load_reg(s, 14);
- gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
tmp = load_cpu_field(spsr);
tcg_gen_addi_i32(addr, addr, 4);
- gen_aa32_st32(s, tmp, addr, get_mem_index(s));
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
if (writeback) {
switch (amode) {
@@ -6458,7 +6483,7 @@ static bool op_load_rr(DisasContext *s, arg_ldst_rr *a,
addr = op_addr_rr_pre(s, a);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop);
disas_set_da_iss(s, mop, issinfo);
/*
@@ -6476,10 +6501,18 @@ static bool op_store_rr(DisasContext *s, arg_ldst_rr *a,
ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite;
TCGv_i32 addr, tmp;
+ /*
+ * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it
+ * is either UNPREDICTABLE or has defined behaviour
+ */
+ if (s->thumb && a->rn == 15) {
+ return false;
+ }
+
addr = op_addr_rr_pre(s, a);
tmp = load_reg(s, a->rt);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, mop | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, mop);
disas_set_da_iss(s, mop, issinfo);
tcg_temp_free_i32(tmp);
@@ -6502,13 +6535,13 @@ static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a)
addr = op_addr_rr_pre(s, a);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
store_reg(s, a->rt, tmp);
tcg_gen_addi_i32(addr, addr, 4);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
store_reg(s, a->rt + 1, tmp);
/* LDRD w/ base writeback is undefined if the registers overlap. */
@@ -6531,13 +6564,13 @@ static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a)
addr = op_addr_rr_pre(s, a);
tmp = load_reg(s, a->rt);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
tcg_gen_addi_i32(addr, addr, 4);
tmp = load_reg(s, a->rt + 1);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
op_addr_rr_post(s, a, addr, -4);
@@ -6602,7 +6635,7 @@ static bool op_load_ri(DisasContext *s, arg_ldst_ri *a,
addr = op_addr_ri_pre(s, a);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop);
disas_set_da_iss(s, mop, issinfo);
/*
@@ -6620,10 +6653,18 @@ static bool op_store_ri(DisasContext *s, arg_ldst_ri *a,
ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite;
TCGv_i32 addr, tmp;
+ /*
+ * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it
+ * is either UNPREDICTABLE or has defined behaviour
+ */
+ if (s->thumb && a->rn == 15) {
+ return false;
+ }
+
addr = op_addr_ri_pre(s, a);
tmp = load_reg(s, a->rt);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, mop | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, mop);
disas_set_da_iss(s, mop, issinfo);
tcg_temp_free_i32(tmp);
@@ -6639,13 +6680,13 @@ static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2)
addr = op_addr_ri_pre(s, a);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
store_reg(s, a->rt, tmp);
tcg_gen_addi_i32(addr, addr, 4);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
store_reg(s, rt2, tmp);
/* LDRD w/ base writeback is undefined if the registers overlap. */
@@ -6678,13 +6719,13 @@ static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2)
addr = op_addr_ri_pre(s, a);
tmp = load_reg(s, a->rt);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
tcg_gen_addi_i32(addr, addr, 4);
tmp = load_reg(s, rt2);
- gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
op_addr_ri_post(s, a, addr, -4);
@@ -6910,7 +6951,7 @@ static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop)
addr = load_reg(s, a->rn);
tmp = load_reg(s, a->rt);
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
- gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), mop | s->be_data);
+ gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN);
disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel | ISSIsWrite);
tcg_temp_free_i32(tmp);
@@ -7066,7 +7107,7 @@ static bool op_lda(DisasContext *s, arg_LDA *a, MemOp mop)
addr = load_reg(s, a->rn);
tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), mop | s->be_data);
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN);
disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel);
tcg_temp_free_i32(addr);
@@ -7858,7 +7899,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n)
} else {
tmp = load_reg(s, i);
}
- gen_aa32_st32(s, tmp, addr, mem_idx);
+ gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_temp_free_i32(tmp);
/* No need to add after the last transfer. */
@@ -7933,7 +7974,7 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n)
}
tmp = tcg_temp_new_i32();
- gen_aa32_ld32u(s, tmp, addr, mem_idx);
+ gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
if (user) {
tmp2 = tcg_const_i32(i);
gen_helper_set_user_reg(cpu_env, tmp2, tmp);
@@ -8250,8 +8291,7 @@ static bool op_tbranch(DisasContext *s, arg_tbranch *a, bool half)
addr = load_reg(s, a->rn);
tcg_gen_add_i32(addr, addr, tmp);
- gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s),
- half ? MO_UW | s->be_data : MO_UB);
+ gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), half ? MO_UW : MO_UB);
tcg_temp_free_i32(addr);
tcg_gen_add_i32(tmp, tmp, tmp);
@@ -8332,10 +8372,10 @@ static bool trans_RFE(DisasContext *s, arg_RFE *a)
/* Load PC into tmp and CPSR into tmp2. */
t1 = tcg_temp_new_i32();
- gen_aa32_ld32u(s, t1, addr, get_mem_index(s));
+ gen_aa32_ld_i32(s, t1, addr, get_mem_index(s), MO_UL | MO_ALIGN);
tcg_gen_addi_i32(addr, addr, 4);
t2 = tcg_temp_new_i32();
- gen_aa32_ld32u(s, t2, addr, get_mem_index(s));
+ gen_aa32_ld_i32(s, t2, addr, get_mem_index(s), MO_UL | MO_ALIGN);
if (a->w) {
/* Base writeback. */
@@ -8836,7 +8876,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUARMState *env = cs->env_ptr;
ARMCPU *cpu = env_archcpu(env);
- uint32_t tb_flags = dc->base.tb->flags;
+ CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb);
uint32_t condexec, core_mmu_idx;
dc->isar = &cpu->isar;
@@ -8848,46 +8888,43 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
*/
dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) &&
!arm_el_is_aa64(env, 3);
- dc->thumb = FIELD_EX32(tb_flags, TBFLAG_AM32, THUMB);
- dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE;
- condexec = FIELD_EX32(tb_flags, TBFLAG_AM32, CONDEXEC);
+ dc->thumb = EX_TBFLAG_AM32(tb_flags, THUMB);
+ dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE;
+ condexec = EX_TBFLAG_AM32(tb_flags, CONDEXEC);
dc->condexec_mask = (condexec & 0xf) << 1;
dc->condexec_cond = condexec >> 4;
- core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX);
+ core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX);
dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx);
dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx);
#if !defined(CONFIG_USER_ONLY)
dc->user = (dc->current_el == 0);
#endif
- dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL);
+ dc->fp_excp_el = EX_TBFLAG_ANY(tb_flags, FPEXC_EL);
+ dc->align_mem = EX_TBFLAG_ANY(tb_flags, ALIGN_MEM);
if (arm_feature(env, ARM_FEATURE_M)) {
dc->vfp_enabled = 1;
dc->be_data = MO_TE;
- dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_M32, HANDLER);
+ dc->v7m_handler_mode = EX_TBFLAG_M32(tb_flags, HANDLER);
dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) &&
regime_is_secure(env, dc->mmu_idx);
- dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_M32, STACKCHECK);
- dc->v8m_fpccr_s_wrong =
- FIELD_EX32(tb_flags, TBFLAG_M32, FPCCR_S_WRONG);
+ dc->v8m_stackcheck = EX_TBFLAG_M32(tb_flags, STACKCHECK);
+ dc->v8m_fpccr_s_wrong = EX_TBFLAG_M32(tb_flags, FPCCR_S_WRONG);
dc->v7m_new_fp_ctxt_needed =
- FIELD_EX32(tb_flags, TBFLAG_M32, NEW_FP_CTXT_NEEDED);
- dc->v7m_lspact = FIELD_EX32(tb_flags, TBFLAG_M32, LSPACT);
+ EX_TBFLAG_M32(tb_flags, NEW_FP_CTXT_NEEDED);
+ dc->v7m_lspact = EX_TBFLAG_M32(tb_flags, LSPACT);
} else {
- dc->be_data =
- FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE;
- dc->debug_target_el =
- FIELD_EX32(tb_flags, TBFLAG_ANY, DEBUG_TARGET_EL);
- dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B);
- dc->hstr_active = FIELD_EX32(tb_flags, TBFLAG_A32, HSTR_ACTIVE);
- dc->ns = FIELD_EX32(tb_flags, TBFLAG_A32, NS);
- dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN);
+ dc->debug_target_el = EX_TBFLAG_ANY(tb_flags, DEBUG_TARGET_EL);
+ dc->sctlr_b = EX_TBFLAG_A32(tb_flags, SCTLR__B);
+ dc->hstr_active = EX_TBFLAG_A32(tb_flags, HSTR_ACTIVE);
+ dc->ns = EX_TBFLAG_A32(tb_flags, NS);
+ dc->vfp_enabled = EX_TBFLAG_A32(tb_flags, VFPEN);
if (arm_feature(env, ARM_FEATURE_XSCALE)) {
- dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR);
+ dc->c15_cpar = EX_TBFLAG_A32(tb_flags, XSCALE_CPAR);
} else {
- dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN);
- dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE);
+ dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN);
+ dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE);
}
}
dc->cp_regs = cpu->cp_regs;
@@ -8908,8 +8945,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
* emit code to generate a software step exception
* end the TB
*/
- dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE);
- dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS);
+ dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE);
+ dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS);
dc->is_ldex = false;
dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK;
@@ -9347,12 +9384,13 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns)
{
DisasContext dc = { };
const TranslatorOps *ops = &arm_translator_ops;
+ CPUARMTBFlags tb_flags = arm_tbflags_from_tb(tb);
- if (FIELD_EX32(tb->flags, TBFLAG_AM32, THUMB)) {
+ if (EX_TBFLAG_AM32(tb_flags, THUMB)) {
ops = &thumb_translator_ops;
}
#ifdef TARGET_AARCH64
- if (FIELD_EX32(tb->flags, TBFLAG_ANY, AARCH64_STATE)) {
+ if (EX_TBFLAG_ANY(tb_flags, AARCH64_STATE)) {
ops = &aarch64_translator_ops;
}
#endif
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 423b0e08df..ccf60c96d8 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -87,6 +87,8 @@ typedef struct DisasContext {
bool bt;
/* True if any CP15 access is trapped by HSTR_EL2 */
bool hstr_active;
+ /* True if memory operations require alignment */
+ bool align_mem;
/*
* >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
* < 0, set by the current instruction.
@@ -202,6 +204,7 @@ void arm_test_cc(DisasCompare *cmp, int cc);
void arm_free_cc(DisasCompare *cmp);
void arm_jump_cc(DisasCompare *cmp, TCGLabel *label);
void arm_gen_test_cc(int cc, TCGLabel *label);
+MemOp pow2_align(unsigned i);
/* Return state of Alternate Half-precision flag, caller frees result */
static inline TCGv_i32 get_ahp_flag(void)
@@ -394,6 +397,17 @@ typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, MemOp);
+/**
+ * arm_tbflags_from_tb:
+ * @tb: the TranslationBlock
+ *
+ * Extract the flag values from @tb.
+ */
+static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb)
+{
+ return (CPUARMTBFlags){ tb->flags, tb->cs_base };
+}
+
/*
* Enum for argument to fpstatus_ptr().
*/
@@ -446,4 +460,28 @@ static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour)
return statusptr;
}
+/**
+ * finalize_memop:
+ * @s: DisasContext
+ * @opc: size+sign+align of the memory operation
+ *
+ * Build the complete MemOp for a memory operation, including alignment
+ * and endianness.
+ *
+ * If (op & MO_AMASK) then the operation already contains the required
+ * alignment, e.g. for AccType_ATOMIC. Otherwise, this an optionally
+ * unaligned operation, e.g. for AccType_NORMAL.
+ *
+ * In the latter case, there are configuration bits that require alignment,
+ * and this is applied here. Note that there is no way to indicate that
+ * no alignment should ever be enforced; this must be handled manually.
+ */
+static inline MemOp finalize_memop(DisasContext *s, MemOp opc)
+{
+ if (s->align_mem && !(opc & MO_AMASK)) {
+ opc |= MO_ALIGN;
+ }
+ return opc | s->be_data;
+}
+
#endif /* TARGET_ARM_TRANSLATE_H */
diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c
index 09de124818..68a55b3bd4 100644
--- a/target/hexagon/arch.c
+++ b/target/hexagon/arch.c
@@ -27,6 +27,97 @@
#define SF_MANTBITS 23
#define float32_nan make_float32(0xffffffff)
+/*
+ * These three tables are used by the cabacdecbin instruction
+ */
+const uint8_t rLPS_table_64x4[64][4] = {
+ {128, 176, 208, 240},
+ {128, 167, 197, 227},
+ {128, 158, 187, 216},
+ {123, 150, 178, 205},
+ {116, 142, 169, 195},
+ {111, 135, 160, 185},
+ {105, 128, 152, 175},
+ {100, 122, 144, 166},
+ {95, 116, 137, 158},
+ {90, 110, 130, 150},
+ {85, 104, 123, 142},
+ {81, 99, 117, 135},
+ {77, 94, 111, 128},
+ {73, 89, 105, 122},
+ {69, 85, 100, 116},
+ {66, 80, 95, 110},
+ {62, 76, 90, 104},
+ {59, 72, 86, 99},
+ {56, 69, 81, 94},
+ {53, 65, 77, 89},
+ {51, 62, 73, 85},
+ {48, 59, 69, 80},
+ {46, 56, 66, 76},
+ {43, 53, 63, 72},
+ {41, 50, 59, 69},
+ {39, 48, 56, 65},
+ {37, 45, 54, 62},
+ {35, 43, 51, 59},
+ {33, 41, 48, 56},
+ {32, 39, 46, 53},
+ {30, 37, 43, 50},
+ {29, 35, 41, 48},
+ {27, 33, 39, 45},
+ {26, 31, 37, 43},
+ {24, 30, 35, 41},
+ {23, 28, 33, 39},
+ {22, 27, 32, 37},
+ {21, 26, 30, 35},
+ {20, 24, 29, 33},
+ {19, 23, 27, 31},
+ {18, 22, 26, 30},
+ {17, 21, 25, 28},
+ {16, 20, 23, 27},
+ {15, 19, 22, 25},
+ {14, 18, 21, 24},
+ {14, 17, 20, 23},
+ {13, 16, 19, 22},
+ {12, 15, 18, 21},
+ {12, 14, 17, 20},
+ {11, 14, 16, 19},
+ {11, 13, 15, 18},
+ {10, 12, 15, 17},
+ {10, 12, 14, 16},
+ {9, 11, 13, 15},
+ {9, 11, 12, 14},
+ {8, 10, 12, 14},
+ {8, 9, 11, 13},
+ {7, 9, 11, 12},
+ {7, 9, 10, 12},
+ {7, 8, 10, 11},
+ {6, 8, 9, 11},
+ {6, 7, 9, 10},
+ {6, 7, 8, 9},
+ {2, 2, 2, 2}
+};
+
+const uint8_t AC_next_state_MPS_64[64] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 62, 63
+};
+
+
+const uint8_t AC_next_state_LPS_64[64] = {
+ 0, 0, 1, 2, 2, 4, 4, 5, 6, 7,
+ 8, 9, 9, 11, 11, 12, 13, 13, 15, 15,
+ 16, 16, 18, 18, 19, 19, 21, 21, 22, 22,
+ 23, 24, 24, 25, 26, 26, 27, 27, 28, 29,
+ 29, 30, 30, 30, 31, 32, 32, 33, 33, 33,
+ 34, 34, 35, 35, 35, 36, 36, 36, 37, 37,
+ 37, 38, 38, 63
+};
+
#define BITS_MASK_8 0x5555555555555555ULL
#define PAIR_MASK_8 0x3333333333333333ULL
#define NYBL_MASK_8 0x0f0f0f0f0f0f0f0fULL
@@ -76,19 +167,6 @@ uint64_t deinterleave(uint64_t src)
return myeven | (myodd << 32);
}
-uint32_t carry_from_add64(uint64_t a, uint64_t b, uint32_t c)
-{
- uint64_t tmpa, tmpb, tmpc;
- tmpa = fGETUWORD(0, a);
- tmpb = fGETUWORD(0, b);
- tmpc = tmpa + tmpb + c;
- tmpa = fGETUWORD(1, a);
- tmpb = fGETUWORD(1, b);
- tmpc = tmpa + tmpb + fGETUWORD(1, tmpc);
- tmpc = fGETUWORD(1, tmpc);
- return tmpc;
-}
-
int32_t conv_round(int32_t a, int n)
{
int64_t val;
@@ -108,7 +186,7 @@ int32_t conv_round(int32_t a, int n)
/* Floating Point Stuff */
-static const int softfloat_roundingmodes[] = {
+static const FloatRoundMode softfloat_roundingmodes[] = {
float_round_nearest_even,
float_round_to_zero,
float_round_down,
@@ -156,12 +234,6 @@ void arch_fpop_end(CPUHexagonState *env)
}
}
-static float32 float32_mul_pow2(float32 a, uint32_t p, float_status *fp_status)
-{
- float32 b = make_float32((SF_BIAS + p) << SF_MANTBITS);
- return float32_mul(a, b, fp_status);
-}
-
int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust,
float_status *fp_status)
{
@@ -200,12 +272,13 @@ int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust,
/* or put Inf in num fixup? */
uint8_t RsV_sign = float32_is_neg(RsV);
uint8_t RtV_sign = float32_is_neg(RtV);
+ /* Check that RsV is NOT infinite before we overwrite it */
+ if (!float32_is_infinity(RsV)) {
+ float_raise(float_flag_divbyzero, fp_status);
+ }
RsV = infinite_float32(RsV_sign ^ RtV_sign);
RtV = float32_one;
RdV = float32_one;
- if (float32_is_infinity(RsV)) {
- float_raise(float_flag_divbyzero, fp_status);
- }
} else if (float32_is_infinity(RtV)) {
RsV = make_float32(0x80000000 & (RsV ^ RtV));
RtV = float32_one;
@@ -230,22 +303,22 @@ int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust,
if ((n_exp - d_exp + SF_BIAS) <= SF_MANTBITS) {
/* Near quotient underflow / inexact Q */
PeV = 0x80;
- RtV = float32_mul_pow2(RtV, -64, fp_status);
- RsV = float32_mul_pow2(RsV, 64, fp_status);
+ RtV = float32_scalbn(RtV, -64, fp_status);
+ RsV = float32_scalbn(RsV, 64, fp_status);
} else if ((n_exp - d_exp + SF_BIAS) > (SF_MAXEXP - 24)) {
/* Near quotient overflow */
PeV = 0x40;
- RtV = float32_mul_pow2(RtV, 32, fp_status);
- RsV = float32_mul_pow2(RsV, -32, fp_status);
+ RtV = float32_scalbn(RtV, 32, fp_status);
+ RsV = float32_scalbn(RsV, -32, fp_status);
} else if (n_exp <= SF_MANTBITS + 2) {
- RtV = float32_mul_pow2(RtV, 64, fp_status);
- RsV = float32_mul_pow2(RsV, 64, fp_status);
+ RtV = float32_scalbn(RtV, 64, fp_status);
+ RsV = float32_scalbn(RsV, 64, fp_status);
} else if (d_exp <= 1) {
- RtV = float32_mul_pow2(RtV, 32, fp_status);
- RsV = float32_mul_pow2(RsV, 32, fp_status);
+ RtV = float32_scalbn(RtV, 32, fp_status);
+ RsV = float32_scalbn(RsV, 32, fp_status);
} else if (d_exp > 252) {
- RtV = float32_mul_pow2(RtV, -32, fp_status);
- RsV = float32_mul_pow2(RsV, -32, fp_status);
+ RtV = float32_scalbn(RtV, -32, fp_status);
+ RsV = float32_scalbn(RsV, -32, fp_status);
}
RdV = 0;
ret = 1;
@@ -265,7 +338,7 @@ int arch_sf_invsqrt_common(float32 *Rs, float32 *Rd, int *adjust,
int r_exp;
int ret = 0;
RsV = *Rs;
- if (float32_is_infinity(RsV)) {
+ if (float32_is_any_nan(RsV)) {
if (extract32(RsV, 22, 1) == 0) {
float_raise(float_flag_invalid, fp_status);
}
@@ -287,7 +360,7 @@ int arch_sf_invsqrt_common(float32 *Rs, float32 *Rd, int *adjust,
/* Basic checks passed */
r_exp = float32_getexp(RsV);
if (r_exp <= 24) {
- RsV = float32_mul_pow2(RsV, 64, fp_status);
+ RsV = float32_scalbn(RsV, 64, fp_status);
PeV = 0xe0;
}
RdV = 0;
@@ -298,3 +371,41 @@ int arch_sf_invsqrt_common(float32 *Rs, float32 *Rd, int *adjust,
*adjust = PeV;
return ret;
}
+
+const uint8_t recip_lookup_table[128] = {
+ 0x0fe, 0x0fa, 0x0f6, 0x0f2, 0x0ef, 0x0eb, 0x0e7, 0x0e4,
+ 0x0e0, 0x0dd, 0x0d9, 0x0d6, 0x0d2, 0x0cf, 0x0cc, 0x0c9,
+ 0x0c6, 0x0c2, 0x0bf, 0x0bc, 0x0b9, 0x0b6, 0x0b3, 0x0b1,
+ 0x0ae, 0x0ab, 0x0a8, 0x0a5, 0x0a3, 0x0a0, 0x09d, 0x09b,
+ 0x098, 0x096, 0x093, 0x091, 0x08e, 0x08c, 0x08a, 0x087,
+ 0x085, 0x083, 0x080, 0x07e, 0x07c, 0x07a, 0x078, 0x075,
+ 0x073, 0x071, 0x06f, 0x06d, 0x06b, 0x069, 0x067, 0x065,
+ 0x063, 0x061, 0x05f, 0x05e, 0x05c, 0x05a, 0x058, 0x056,
+ 0x054, 0x053, 0x051, 0x04f, 0x04e, 0x04c, 0x04a, 0x049,
+ 0x047, 0x045, 0x044, 0x042, 0x040, 0x03f, 0x03d, 0x03c,
+ 0x03a, 0x039, 0x037, 0x036, 0x034, 0x033, 0x032, 0x030,
+ 0x02f, 0x02d, 0x02c, 0x02b, 0x029, 0x028, 0x027, 0x025,
+ 0x024, 0x023, 0x021, 0x020, 0x01f, 0x01e, 0x01c, 0x01b,
+ 0x01a, 0x019, 0x017, 0x016, 0x015, 0x014, 0x013, 0x012,
+ 0x011, 0x00f, 0x00e, 0x00d, 0x00c, 0x00b, 0x00a, 0x009,
+ 0x008, 0x007, 0x006, 0x005, 0x004, 0x003, 0x002, 0x000,
+};
+
+const uint8_t invsqrt_lookup_table[128] = {
+ 0x069, 0x066, 0x063, 0x061, 0x05e, 0x05b, 0x059, 0x057,
+ 0x054, 0x052, 0x050, 0x04d, 0x04b, 0x049, 0x047, 0x045,
+ 0x043, 0x041, 0x03f, 0x03d, 0x03b, 0x039, 0x037, 0x036,
+ 0x034, 0x032, 0x030, 0x02f, 0x02d, 0x02c, 0x02a, 0x028,
+ 0x027, 0x025, 0x024, 0x022, 0x021, 0x01f, 0x01e, 0x01d,
+ 0x01b, 0x01a, 0x019, 0x017, 0x016, 0x015, 0x014, 0x012,
+ 0x011, 0x010, 0x00f, 0x00d, 0x00c, 0x00b, 0x00a, 0x009,
+ 0x008, 0x007, 0x006, 0x005, 0x004, 0x003, 0x002, 0x001,
+ 0x0fe, 0x0fa, 0x0f6, 0x0f3, 0x0ef, 0x0eb, 0x0e8, 0x0e4,
+ 0x0e1, 0x0de, 0x0db, 0x0d7, 0x0d4, 0x0d1, 0x0ce, 0x0cb,
+ 0x0c9, 0x0c6, 0x0c3, 0x0c0, 0x0be, 0x0bb, 0x0b8, 0x0b6,
+ 0x0b3, 0x0b1, 0x0af, 0x0ac, 0x0aa, 0x0a8, 0x0a5, 0x0a3,
+ 0x0a1, 0x09f, 0x09d, 0x09b, 0x099, 0x097, 0x095, 0x093,
+ 0x091, 0x08f, 0x08d, 0x08b, 0x089, 0x087, 0x086, 0x084,
+ 0x082, 0x080, 0x07f, 0x07d, 0x07b, 0x07a, 0x078, 0x077,
+ 0x075, 0x074, 0x072, 0x071, 0x06f, 0x06e, 0x06c, 0x06b,
+};
diff --git a/target/hexagon/arch.h b/target/hexagon/arch.h
index 1f7f03693a..70918065d3 100644
--- a/target/hexagon/arch.h
+++ b/target/hexagon/arch.h
@@ -20,9 +20,12 @@
#include "qemu/int128.h"
+extern const uint8_t rLPS_table_64x4[64][4];
+extern const uint8_t AC_next_state_MPS_64[64];
+extern const uint8_t AC_next_state_LPS_64[64];
+
uint64_t interleave(uint32_t odd, uint32_t even);
uint64_t deinterleave(uint64_t src);
-uint32_t carry_from_add64(uint64_t a, uint64_t b, uint32_t c);
int32_t conv_round(int32_t a, int n);
void arch_fpop_start(CPUHexagonState *env);
void arch_fpop_end(CPUHexagonState *env);
@@ -31,4 +34,8 @@ int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd,
int arch_sf_invsqrt_common(float32 *Rs, float32 *Rd, int *adjust,
float_status *fp_status);
+extern const uint8_t recip_lookup_table[128];
+
+extern const uint8_t invsqrt_lookup_table[128];
+
#endif
diff --git a/target/hexagon/conv_emu.c b/target/hexagon/conv_emu.c
deleted file mode 100644
index 3985b1032a..0000000000
--- a/target/hexagon/conv_emu.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
- *
- * 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/>.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/host-utils.h"
-#include "fpu/softfloat.h"
-#include "macros.h"
-#include "conv_emu.h"
-
-#define LL_MAX_POS 0x7fffffffffffffffULL
-#define MAX_POS 0x7fffffffU
-
-static uint64_t conv_f64_to_8u_n(float64 in, int will_negate,
- float_status *fp_status)
-{
- uint8_t sign = float64_is_neg(in);
- if (float64_is_infinity(in)) {
- float_raise(float_flag_invalid, fp_status);
- if (float64_is_neg(in)) {
- return 0ULL;
- } else {
- return ~0ULL;
- }
- }
- if (float64_is_any_nan(in)) {
- float_raise(float_flag_invalid, fp_status);
- return ~0ULL;
- }
- if (float64_is_zero(in)) {
- return 0;
- }
- if (sign) {
- float_raise(float_flag_invalid, fp_status);
- return 0;
- }
- if (float64_lt(in, float64_half, fp_status)) {
- /* Near zero, captures large fracshifts, denorms, etc */
- float_raise(float_flag_inexact, fp_status);
- switch (get_float_rounding_mode(fp_status)) {
- case float_round_down:
- if (will_negate) {
- return 1;
- } else {
- return 0;
- }
- case float_round_up:
- if (!will_negate) {
- return 1;
- } else {
- return 0;
- }
- default:
- return 0; /* nearest or towards zero */
- }
- }
- return float64_to_uint64(in, fp_status);
-}
-
-static void clr_float_exception_flags(uint8_t flag, float_status *fp_status)
-{
- uint8_t flags = fp_status->float_exception_flags;
- flags &= ~flag;
- set_float_exception_flags(flags, fp_status);
-}
-
-static uint32_t conv_df_to_4u_n(float64 fp64, int will_negate,
- float_status *fp_status)
-{
- uint64_t tmp;
- tmp = conv_f64_to_8u_n(fp64, will_negate, fp_status);
- if (tmp > 0x00000000ffffffffULL) {
- clr_float_exception_flags(float_flag_inexact, fp_status);
- float_raise(float_flag_invalid, fp_status);
- return ~0U;
- }
- return (uint32_t)tmp;
-}
-
-uint64_t conv_df_to_8u(float64 in, float_status *fp_status)
-{
- return conv_f64_to_8u_n(in, 0, fp_status);
-}
-
-uint32_t conv_df_to_4u(float64 in, float_status *fp_status)
-{
- return conv_df_to_4u_n(in, 0, fp_status);
-}
-
-int64_t conv_df_to_8s(float64 in, float_status *fp_status)
-{
- uint8_t sign = float64_is_neg(in);
- uint64_t tmp;
- if (float64_is_any_nan(in)) {
- float_raise(float_flag_invalid, fp_status);
- return -1;
- }
- if (sign) {
- float64 minus_fp64 = float64_abs(in);
- tmp = conv_f64_to_8u_n(minus_fp64, 1, fp_status);
- } else {
- tmp = conv_f64_to_8u_n(in, 0, fp_status);
- }
- if (tmp > (LL_MAX_POS + sign)) {
- clr_float_exception_flags(float_flag_inexact, fp_status);
- float_raise(float_flag_invalid, fp_status);
- tmp = (LL_MAX_POS + sign);
- }
- if (sign) {
- return -tmp;
- } else {
- return tmp;
- }
-}
-
-int32_t conv_df_to_4s(float64 in, float_status *fp_status)
-{
- uint8_t sign = float64_is_neg(in);
- uint64_t tmp;
- if (float64_is_any_nan(in)) {
- float_raise(float_flag_invalid, fp_status);
- return -1;
- }
- if (sign) {
- float64 minus_fp64 = float64_abs(in);
- tmp = conv_f64_to_8u_n(minus_fp64, 1, fp_status);
- } else {
- tmp = conv_f64_to_8u_n(in, 0, fp_status);
- }
- if (tmp > (MAX_POS + sign)) {
- clr_float_exception_flags(float_flag_inexact, fp_status);
- float_raise(float_flag_invalid, fp_status);
- tmp = (MAX_POS + sign);
- }
- if (sign) {
- return -tmp;
- } else {
- return tmp;
- }
-}
-
-uint64_t conv_sf_to_8u(float32 in, float_status *fp_status)
-{
- float64 fp64 = float32_to_float64(in, fp_status);
- return conv_df_to_8u(fp64, fp_status);
-}
-
-uint32_t conv_sf_to_4u(float32 in, float_status *fp_status)
-{
- float64 fp64 = float32_to_float64(in, fp_status);
- return conv_df_to_4u(fp64, fp_status);
-}
-
-int64_t conv_sf_to_8s(float32 in, float_status *fp_status)
-{
- float64 fp64 = float32_to_float64(in, fp_status);
- return conv_df_to_8s(fp64, fp_status);
-}
-
-int32_t conv_sf_to_4s(float32 in, float_status *fp_status)
-{
- float64 fp64 = float32_to_float64(in, fp_status);
- return conv_df_to_4s(fp64, fp_status);
-}
diff --git a/target/hexagon/conv_emu.h b/target/hexagon/conv_emu.h
deleted file mode 100644
index cade9de91f..0000000000
--- a/target/hexagon/conv_emu.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
- *
- * 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/>.
- */
-
-#ifndef HEXAGON_CONV_EMU_H
-#define HEXAGON_CONV_EMU_H
-
-uint64_t conv_sf_to_8u(float32 in, float_status *fp_status);
-uint32_t conv_sf_to_4u(float32 in, float_status *fp_status);
-int64_t conv_sf_to_8s(float32 in, float_status *fp_status);
-int32_t conv_sf_to_4s(float32 in, float_status *fp_status);
-
-uint64_t conv_df_to_8u(float64 in, float_status *fp_status);
-uint32_t conv_df_to_4u(float64 in, float_status *fp_status);
-int64_t conv_df_to_8s(float64 in, float_status *fp_status);
-int32_t conv_df_to_4s(float64 in, float_status *fp_status);
-
-#endif
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index b0b3040dd1..ff44fd6637 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -23,6 +23,7 @@
#include "exec/exec-all.h"
#include "qapi/error.h"
#include "hw/qdev-properties.h"
+#include "fpu/softfloat-helpers.h"
static void hexagon_v67_cpu_init(Object *obj)
{
@@ -69,10 +70,9 @@ const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = {
* stacks at different locations. This is used to compensate so the diff is
* cleaner.
*/
-static inline target_ulong adjust_stack_ptrs(CPUHexagonState *env,
- target_ulong addr)
+static target_ulong adjust_stack_ptrs(CPUHexagonState *env, target_ulong addr)
{
- HexagonCPU *cpu = container_of(env, HexagonCPU, env);
+ HexagonCPU *cpu = env_archcpu(env);
target_ulong stack_adjust = cpu->lldb_stack_adjust;
target_ulong stack_start = env->stack_start;
target_ulong stack_size = 0x10000;
@@ -88,7 +88,7 @@ static inline target_ulong adjust_stack_ptrs(CPUHexagonState *env,
}
/* HEX_REG_P3_0 (aka C4) is an alias for the predicate registers */
-static inline target_ulong read_p3_0(CPUHexagonState *env)
+static target_ulong read_p3_0(CPUHexagonState *env)
{
int32_t control_reg = 0;
int i;
@@ -116,7 +116,7 @@ static void print_reg(FILE *f, CPUHexagonState *env, int regnum)
static void hexagon_dump(CPUHexagonState *env, FILE *f)
{
- HexagonCPU *cpu = container_of(env, HexagonCPU, env);
+ HexagonCPU *cpu = env_archcpu(env);
if (cpu->lldb_compat) {
/*
@@ -206,8 +206,12 @@ static void hexagon_cpu_reset(DeviceState *dev)
CPUState *cs = CPU(dev);
HexagonCPU *cpu = HEXAGON_CPU(cs);
HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(cpu);
+ CPUHexagonState *env = &cpu->env;
mcc->parent_reset(dev);
+
+ set_default_nan_mode(1, &env->fp_status);
+ set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status);
}
static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info)
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index e04eac591c..2855dd3881 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -127,11 +127,6 @@ typedef struct HexagonCPU {
target_ulong lldb_stack_adjust;
} HexagonCPU;
-static inline HexagonCPU *hexagon_env_get_cpu(CPUHexagonState *env)
-{
- return container_of(env, HexagonCPU, env);
-}
-
#include "cpu_bits.h"
#define cpu_signal_handler cpu_hexagon_signal_handler
diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h
index 96af834d0e..96fef71729 100644
--- a/target/hexagon/cpu_bits.h
+++ b/target/hexagon/cpu_bits.h
@@ -47,7 +47,7 @@ static inline uint32_t iclass_bits(uint32_t encoding)
return iclass;
}
-static inline int is_packet_end(uint32_t endocing)
+static inline bool is_packet_end(uint32_t endocing)
{
uint32_t bits = parse_bits(endocing);
return ((bits == 0x3) || (bits == 0x0));
diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c
index c9bacaa1ee..dffe1d1972 100644
--- a/target/hexagon/decode.c
+++ b/target/hexagon/decode.c
@@ -48,8 +48,8 @@ enum {
DEF_REGMAP(R_16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23)
DEF_REGMAP(R__8, 8, 0, 2, 4, 6, 16, 18, 20, 22)
-#define DECODE_MAPPED_REG(REGNO, NAME) \
- insn->regno[REGNO] = DECODE_REGISTER_##NAME[insn->regno[REGNO]];
+#define DECODE_MAPPED_REG(OPNUM, NAME) \
+ insn->regno[OPNUM] = DECODE_REGISTER_##NAME[insn->regno[OPNUM]];
typedef struct {
const struct DectreeTable *table_link;
@@ -340,8 +340,8 @@ static void decode_split_cmpjump(Packet *pkt)
if (GET_ATTRIB(pkt->insn[i].opcode, A_NEWCMPJUMP)) {
last = pkt->num_insns;
pkt->insn[last] = pkt->insn[i]; /* copy the instruction */
- pkt->insn[last].part1 = 1; /* last instruction does the CMP */
- pkt->insn[i].part1 = 0; /* existing instruction does the JUMP */
+ pkt->insn[last].part1 = true; /* last insn does the CMP */
+ pkt->insn[i].part1 = false; /* existing insn does the JUMP */
pkt->num_insns++;
}
}
@@ -354,7 +354,7 @@ static void decode_split_cmpjump(Packet *pkt)
}
}
-static inline int decode_opcode_can_jump(int opcode)
+static bool decode_opcode_can_jump(int opcode)
{
if ((GET_ATTRIB(opcode, A_JUMP)) ||
(GET_ATTRIB(opcode, A_CALL)) ||
@@ -362,15 +362,15 @@ static inline int decode_opcode_can_jump(int opcode)
(opcode == J2_pause)) {
/* Exception to A_JUMP attribute */
if (opcode == J4_hintjumpr) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
- return 0;
+ return false;
}
-static inline int decode_opcode_ends_loop(int opcode)
+static bool decode_opcode_ends_loop(int opcode)
{
return GET_ATTRIB(opcode, A_HWLOOP0_END) ||
GET_ATTRIB(opcode, A_HWLOOP1_END);
@@ -383,9 +383,9 @@ static void decode_set_insn_attr_fields(Packet *pkt)
int numinsns = pkt->num_insns;
uint16_t opcode;
- pkt->pkt_has_cof = 0;
- pkt->pkt_has_endloop = 0;
- pkt->pkt_has_dczeroa = 0;
+ pkt->pkt_has_cof = false;
+ pkt->pkt_has_endloop = false;
+ pkt->pkt_has_dczeroa = false;
for (i = 0; i < numinsns; i++) {
opcode = pkt->insn[i].opcode;
@@ -394,14 +394,14 @@ static void decode_set_insn_attr_fields(Packet *pkt)
}
if (GET_ATTRIB(opcode, A_DCZEROA)) {
- pkt->pkt_has_dczeroa = 1;
+ pkt->pkt_has_dczeroa = true;
}
if (GET_ATTRIB(opcode, A_STORE)) {
if (pkt->insn[i].slot == 0) {
- pkt->pkt_has_store_s0 = 1;
+ pkt->pkt_has_store_s0 = true;
} else {
- pkt->pkt_has_store_s1 = 1;
+ pkt->pkt_has_store_s1 = true;
}
}
@@ -422,9 +422,9 @@ static void decode_set_insn_attr_fields(Packet *pkt)
*/
static void decode_shuffle_for_execution(Packet *packet)
{
- int changed = 0;
+ bool changed = false;
int i;
- int flag; /* flag means we've seen a non-memory instruction */
+ bool flag; /* flag means we've seen a non-memory instruction */
int n_mems;
int last_insn = packet->num_insns - 1;
@@ -437,7 +437,7 @@ static void decode_shuffle_for_execution(Packet *packet)
}
do {
- changed = 0;
+ changed = false;
/*
* Stores go last, must not reorder.
* Cannot shuffle stores past loads, either.
@@ -445,13 +445,13 @@ static void decode_shuffle_for_execution(Packet *packet)
* then a store, shuffle the store to the front. Don't shuffle
* stores wrt each other or a load.
*/
- for (flag = n_mems = 0, i = last_insn; i >= 0; i--) {
+ for (flag = false, n_mems = 0, i = last_insn; i >= 0; i--) {
int opcode = packet->insn[i].opcode;
if (flag && GET_ATTRIB(opcode, A_STORE)) {
decode_send_insn_to(packet, i, last_insn - n_mems);
n_mems++;
- changed = 1;
+ changed = true;
} else if (GET_ATTRIB(opcode, A_STORE)) {
n_mems++;
} else if (GET_ATTRIB(opcode, A_LOAD)) {
@@ -466,7 +466,7 @@ static void decode_shuffle_for_execution(Packet *packet)
* a .new value
*/
} else {
- flag = 1;
+ flag = true;
}
}
@@ -474,7 +474,7 @@ static void decode_shuffle_for_execution(Packet *packet)
continue;
}
/* Compares go first, may be reordered wrt each other */
- for (flag = 0, i = 0; i < last_insn + 1; i++) {
+ for (flag = false, i = 0; i < last_insn + 1; i++) {
int opcode = packet->insn[i].opcode;
if ((strstr(opcode_wregs[opcode], "Pd4") ||
@@ -483,7 +483,7 @@ static void decode_shuffle_for_execution(Packet *packet)
/* This should be a compare (not a store conditional) */
if (flag) {
decode_send_insn_to(packet, i, 0);
- changed = 1;
+ changed = true;
continue;
}
} else if (GET_ATTRIB(opcode, A_IMPLICIT_WRITES_P3) &&
@@ -495,18 +495,18 @@ static void decode_shuffle_for_execution(Packet *packet)
*/
if (flag) {
decode_send_insn_to(packet, i, 0);
- changed = 1;
+ changed = true;
continue;
}
} else if (GET_ATTRIB(opcode, A_IMPLICIT_WRITES_P0) &&
!GET_ATTRIB(opcode, A_NEWCMPJUMP)) {
if (flag) {
decode_send_insn_to(packet, i, 0);
- changed = 1;
+ changed = true;
continue;
}
} else {
- flag = 1;
+ flag = true;
}
}
if (changed) {
@@ -543,7 +543,7 @@ static void decode_apply_extenders(Packet *packet)
int i;
for (i = 0; i < packet->num_insns; i++) {
if (GET_ATTRIB(packet->insn[i].opcode, A_IT_EXTENDER)) {
- packet->insn[i + 1].extension_valid = 1;
+ packet->insn[i + 1].extension_valid = true;
apply_extender(packet, i + 1, packet->insn[i].immed[0]);
}
}
@@ -764,7 +764,7 @@ static void decode_add_endloop_insn(Insn *insn, int loopnum)
}
}
-static inline int decode_parsebits_is_loopend(uint32_t encoding32)
+static bool decode_parsebits_is_loopend(uint32_t encoding32)
{
uint32_t bits = parse_bits(encoding32);
return bits == 0x2;
@@ -775,8 +775,11 @@ decode_set_slot_number(Packet *pkt)
{
int slot;
int i;
- int hit_mem_insn = 0;
- int hit_duplex = 0;
+ bool hit_mem_insn = false;
+ bool hit_duplex = false;
+ bool slot0_found = false;
+ bool slot1_found = false;
+ int slot1_iidx = 0;
/*
* The slots are encoded in reverse order
@@ -801,7 +804,7 @@ decode_set_slot_number(Packet *pkt)
if ((GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE) ||
GET_ATTRIB(pkt->insn[i].opcode, A_MEMLIKE_PACKET_RULES)) &&
!hit_mem_insn) {
- hit_mem_insn = 1;
+ hit_mem_insn = true;
pkt->insn[i].slot = 0;
continue;
}
@@ -818,7 +821,7 @@ decode_set_slot_number(Packet *pkt)
for (i = pkt->num_insns - 1; i >= 0; i--) {
/* First subinsn always goes to slot 0 */
if (GET_ATTRIB(pkt->insn[i].opcode, A_SUBINSN) && !hit_duplex) {
- hit_duplex = 1;
+ hit_duplex = true;
pkt->insn[i].slot = 0;
continue;
}
@@ -830,13 +833,10 @@ decode_set_slot_number(Packet *pkt)
}
/* Fix the exceptions - slot 1 is never empty, always aligns to slot 0 */
- int slot0_found = 0;
- int slot1_found = 0;
- int slot1_iidx = 0;
for (i = pkt->num_insns - 1; i >= 0; i--) {
/* Is slot0 used? */
if (pkt->insn[i].slot == 0) {
- int is_endloop = (pkt->insn[i].opcode == J2_endloop01);
+ bool is_endloop = (pkt->insn[i].opcode == J2_endloop01);
is_endloop |= (pkt->insn[i].opcode == J2_endloop0);
is_endloop |= (pkt->insn[i].opcode == J2_endloop1);
@@ -845,17 +845,17 @@ decode_set_slot_number(Packet *pkt)
* slot0 for endloop
*/
if (!is_endloop) {
- slot0_found = 1;
+ slot0_found = true;
}
}
/* Is slot1 used? */
if (pkt->insn[i].slot == 1) {
- slot1_found = 1;
+ slot1_found = true;
slot1_iidx = i;
}
}
/* Is slot0 empty and slot1 used? */
- if ((slot0_found == 0) && (slot1_found == 1)) {
+ if ((!slot0_found) && slot1_found) {
/* Then push it to slot0 */
pkt->insn[slot1_iidx].slot = 0;
}
@@ -873,7 +873,7 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt,
{
int num_insns = 0;
int words_read = 0;
- int end_of_packet = 0;
+ bool end_of_packet = false;
int new_insns = 0;
uint32_t encoding32;
@@ -890,7 +890,7 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt,
* decode works
*/
if (pkt->insn[num_insns].opcode == A4_ext) {
- pkt->insn[num_insns + 1].extension_valid = 1;
+ pkt->insn[num_insns + 1].extension_valid = true;
}
num_insns += new_insns;
words_read++;
@@ -913,7 +913,7 @@ int decode_packet(int max_words, const uint32_t *words, Packet *pkt,
decode_add_endloop_insn(&pkt->insn[pkt->num_insns++], 0);
}
if (words_read >= 3) {
- uint32_t has_loop0, has_loop1;
+ bool has_loop0, has_loop1;
has_loop0 = decode_parsebits_is_loopend(words[0]);
has_loop1 = decode_parsebits_is_loopend(words[1]);
if (has_loop0 && has_loop1) {
diff --git a/target/hexagon/fma_emu.c b/target/hexagon/fma_emu.c
index 842d903710..d3b45d494f 100644
--- a/target/hexagon/fma_emu.c
+++ b/target/hexagon/fma_emu.c
@@ -19,7 +19,6 @@
#include "qemu/int128.h"
#include "fpu/softfloat.h"
#include "macros.h"
-#include "conv_emu.h"
#include "fma_emu.h"
#define DF_INF_EXP 0x7ff
@@ -64,7 +63,7 @@ typedef union {
};
} Float;
-static inline uint64_t float64_getmant(float64 f64)
+static uint64_t float64_getmant(float64 f64)
{
Double a = { .i = f64 };
if (float64_is_normal(f64)) {
@@ -91,7 +90,7 @@ int32_t float64_getexp(float64 f64)
return -1;
}
-static inline uint64_t float32_getmant(float32 f32)
+static uint64_t float32_getmant(float32 f32)
{
Float a = { .i = f32 };
if (float32_is_normal(f32)) {
@@ -118,17 +117,17 @@ int32_t float32_getexp(float32 f32)
return -1;
}
-static inline uint32_t int128_getw0(Int128 x)
+static uint32_t int128_getw0(Int128 x)
{
return int128_getlo(x);
}
-static inline uint32_t int128_getw1(Int128 x)
+static uint32_t int128_getw1(Int128 x)
{
return int128_getlo(x) >> 32;
}
-static inline Int128 int128_mul_6464(uint64_t ai, uint64_t bi)
+static Int128 int128_mul_6464(uint64_t ai, uint64_t bi)
{
Int128 a, b;
uint64_t pp0, pp1a, pp1b, pp1s, pp2;
@@ -152,7 +151,7 @@ static inline Int128 int128_mul_6464(uint64_t ai, uint64_t bi)
return int128_make128(ret_low, pp2 + (pp1s >> 32));
}
-static inline Int128 int128_sub_borrow(Int128 a, Int128 b, int borrow)
+static Int128 int128_sub_borrow(Int128 a, Int128 b, int borrow)
{
Int128 ret = int128_sub(a, b);
if (borrow != 0) {
@@ -170,7 +169,7 @@ typedef struct {
uint8_t sticky;
} Accum;
-static inline void accum_init(Accum *p)
+static void accum_init(Accum *p)
{
p->mant = int128_zero();
p->exp = 0;
@@ -180,7 +179,7 @@ static inline void accum_init(Accum *p)
p->sticky = 0;
}
-static inline Accum accum_norm_left(Accum a)
+static Accum accum_norm_left(Accum a)
{
a.exp--;
a.mant = int128_lshift(a.mant, 1);
@@ -190,6 +189,7 @@ static inline Accum accum_norm_left(Accum a)
return a;
}
+/* This function is marked inline for performance reasons */
static inline Accum accum_norm_right(Accum a, int amt)
{
if (amt > 130) {
@@ -226,7 +226,7 @@ static inline Accum accum_norm_right(Accum a, int amt)
*/
static Accum accum_add(Accum a, Accum b);
-static inline Accum accum_sub(Accum a, Accum b, int negate)
+static Accum accum_sub(Accum a, Accum b, int negate)
{
Accum ret;
accum_init(&ret);
@@ -329,7 +329,7 @@ static Accum accum_add(Accum a, Accum b)
}
/* Return an infinity with requested sign */
-static inline float64 infinite_float64(uint8_t sign)
+static float64 infinite_float64(uint8_t sign)
{
if (sign) {
return make_float64(DF_MINUS_INF);
@@ -339,7 +339,7 @@ static inline float64 infinite_float64(uint8_t sign)
}
/* Return a maximum finite value with requested sign */
-static inline float64 maxfinite_float64(uint8_t sign)
+static float64 maxfinite_float64(uint8_t sign)
{
if (sign) {
return make_float64(DF_MINUS_MAXF);
@@ -349,7 +349,7 @@ static inline float64 maxfinite_float64(uint8_t sign)
}
/* Return a zero value with requested sign */
-static inline float64 zero_float64(uint8_t sign)
+static float64 zero_float64(uint8_t sign)
{
if (sign) {
return make_float64(0x8000000000000000);
@@ -369,7 +369,7 @@ float32 infinite_float32(uint8_t sign)
}
/* Return a maximum finite value with the requested sign */
-static inline float32 maxfinite_float32(uint8_t sign)
+static float32 maxfinite_float32(uint8_t sign)
{
if (sign) {
return make_float32(SF_MINUS_MAXF);
@@ -379,7 +379,7 @@ static inline float32 maxfinite_float32(uint8_t sign)
}
/* Return a zero value with requested sign */
-static inline float32 zero_float32(uint8_t sign)
+static float32 zero_float32(uint8_t sign)
{
if (sign) {
return make_float32(0x80000000);
@@ -389,7 +389,7 @@ static inline float32 zero_float32(uint8_t sign)
}
#define GEN_XF_ROUND(SUFFIX, MANTBITS, INF_EXP, INTERNAL_TYPE) \
-static inline SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \
+static SUFFIX accum_round_##SUFFIX(Accum a, float_status * fp_status) \
{ \
if ((int128_gethi(a.mant) == 0) && (int128_getlo(a.mant) == 0) \
&& ((a.guard | a.round | a.sticky) == 0)) { \
@@ -526,8 +526,8 @@ static bool is_inf_prod(float64 a, float64 b)
(float64_is_infinity(b) && is_finite(a) && (!float64_is_zero(a))));
}
-static inline float64 special_fma(float64 a, float64 b, float64 c,
- float_status *fp_status)
+static float64 special_fma(float64 a, float64 b, float64 c,
+ float_status *fp_status)
{
float64 ret = make_float64(0);
@@ -586,8 +586,8 @@ static inline float64 special_fma(float64 a, float64 b, float64 c,
g_assert_not_reached();
}
-static inline float32 special_fmaf(float32 a, float32 b, float32 c,
- float_status *fp_status)
+static float32 special_fmaf(float32 a, float32 b, float32 c,
+ float_status *fp_status)
{
float64 aa, bb, cc;
aa = float32_to_float64(a, fp_status);
diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h
index e044deaff2..18fcdbc7e4 100644
--- a/target/hexagon/gen_tcg.h
+++ b/target/hexagon/gen_tcg.h
@@ -37,7 +37,10 @@
* _sp stack pointer relative r0 = memw(r29+#12)
* _ap absolute set r0 = memw(r1=##variable)
* _pr post increment register r0 = memw(r1++m1)
+ * _pbr post increment bit reverse r0 = memw(r1++m1:brev)
* _pi post increment immediate r0 = memb(r1++#1)
+ * _pci post increment circular immediate r0 = memw(r1++#4:circ(m0))
+ * _pcr post increment circular register r0 = memw(r1++I:circ(m0))
*/
/* Macros for complex addressing modes */
@@ -51,12 +54,32 @@
fEA_REG(RxV); \
fPM_M(RxV, MuV); \
} while (0)
+#define GET_EA_pbr \
+ do { \
+ gen_helper_fbrev(EA, RxV); \
+ tcg_gen_add_tl(RxV, RxV, MuV); \
+ } while (0)
#define GET_EA_pi \
do { \
fEA_REG(RxV); \
fPM_I(RxV, siV); \
} while (0)
-
+#define GET_EA_pci \
+ do { \
+ TCGv tcgv_siV = tcg_const_tl(siV); \
+ tcg_gen_mov_tl(EA, RxV); \
+ gen_helper_fcircadd(RxV, RxV, tcgv_siV, MuV, \
+ hex_gpr[HEX_REG_CS0 + MuN]); \
+ tcg_temp_free(tcgv_siV); \
+ } while (0)
+#define GET_EA_pcr(SHIFT) \
+ do { \
+ TCGv ireg = tcg_temp_new(); \
+ tcg_gen_mov_tl(EA, RxV); \
+ gen_read_ireg(ireg, MuV, (SHIFT)); \
+ gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \
+ tcg_temp_free(ireg); \
+ } while (0)
/* Instructions with multiple definitions */
#define fGEN_TCG_LOAD_AP(RES, SIZE, SIGN) \
@@ -80,20 +103,230 @@
#define fGEN_TCG_L4_loadrd_ap(SHORTCODE) \
fGEN_TCG_LOAD_AP(RddV, 8, u)
+#define fGEN_TCG_L2_loadrub_pci(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrb_pci(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadruh_pci(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrh_pci(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadri_pci(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrd_pci(SHORTCODE) SHORTCODE
+
+#define fGEN_TCG_LOAD_pcr(SHIFT, LOAD) \
+ do { \
+ TCGv ireg = tcg_temp_new(); \
+ tcg_gen_mov_tl(EA, RxV); \
+ gen_read_ireg(ireg, MuV, SHIFT); \
+ gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \
+ LOAD; \
+ tcg_temp_free(ireg); \
+ } while (0)
+
+#define fGEN_TCG_L2_loadrub_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(0, fLOAD(1, 1, u, EA, RdV))
+#define fGEN_TCG_L2_loadrb_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(0, fLOAD(1, 1, s, EA, RdV))
+#define fGEN_TCG_L2_loadruh_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(1, fLOAD(1, 2, u, EA, RdV))
+#define fGEN_TCG_L2_loadrh_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(1, fLOAD(1, 2, s, EA, RdV))
+#define fGEN_TCG_L2_loadri_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(2, fLOAD(1, 4, u, EA, RdV))
+#define fGEN_TCG_L2_loadrd_pcr(SHORTCODE) \
+ fGEN_TCG_LOAD_pcr(3, fLOAD(1, 8, u, EA, RddV))
+
#define fGEN_TCG_L2_loadrub_pr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrub_pbr(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrub_pi(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrb_pr(SHORTCODE) SHORTCODE
-#define fGEN_TCG_L2_loadrb_pi(SHORTCODE) SHORTCODE;
+#define fGEN_TCG_L2_loadrb_pbr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrb_pi(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadruh_pr(SHORTCODE) SHORTCODE
-#define fGEN_TCG_L2_loadruh_pi(SHORTCODE) SHORTCODE;
+#define fGEN_TCG_L2_loadruh_pbr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadruh_pi(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrh_pr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrh_pbr(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrh_pi(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadri_pr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadri_pbr(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadri_pi(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrd_pr(SHORTCODE) SHORTCODE
+#define fGEN_TCG_L2_loadrd_pbr(SHORTCODE) SHORTCODE
#define fGEN_TCG_L2_loadrd_pi(SHORTCODE) SHORTCODE
/*
+ * These instructions load 2 bytes and places them in
+ * two halves of the destination register.
+ * The GET_EA macro determines the addressing mode.
+ * The SIGN argument determines whether to zero-extend or
+ * sign-extend.
+ */
+#define fGEN_TCG_loadbXw2(GET_EA, SIGN) \
+ do { \
+ TCGv tmp = tcg_temp_new(); \
+ TCGv byte = tcg_temp_new(); \
+ GET_EA; \
+ fLOAD(1, 2, u, EA, tmp); \
+ tcg_gen_movi_tl(RdV, 0); \
+ for (int i = 0; i < 2; i++) { \
+ gen_set_half(i, RdV, gen_get_byte(byte, i, tmp, (SIGN))); \
+ } \
+ tcg_temp_free(tmp); \
+ tcg_temp_free(byte); \
+ } while (0)
+
+#define fGEN_TCG_L2_loadbzw2_io(SHORTCODE) \
+ fGEN_TCG_loadbXw2(fEA_RI(RsV, siV), false)
+#define fGEN_TCG_L4_loadbzw2_ur(SHORTCODE) \
+ fGEN_TCG_loadbXw2(fEA_IRs(UiV, RtV, uiV), false)
+#define fGEN_TCG_L2_loadbsw2_io(SHORTCODE) \
+ fGEN_TCG_loadbXw2(fEA_RI(RsV, siV), true)
+#define fGEN_TCG_L4_loadbsw2_ur(SHORTCODE) \
+ fGEN_TCG_loadbXw2(fEA_IRs(UiV, RtV, uiV), true)
+#define fGEN_TCG_L4_loadbzw2_ap(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_ap, false)
+#define fGEN_TCG_L2_loadbzw2_pr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pr, false)
+#define fGEN_TCG_L2_loadbzw2_pbr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pbr, false)
+#define fGEN_TCG_L2_loadbzw2_pi(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pi, false)
+#define fGEN_TCG_L4_loadbsw2_ap(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_ap, true)
+#define fGEN_TCG_L2_loadbsw2_pr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pr, true)
+#define fGEN_TCG_L2_loadbsw2_pbr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pbr, true)
+#define fGEN_TCG_L2_loadbsw2_pi(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pi, true)
+#define fGEN_TCG_L2_loadbzw2_pci(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pci, false)
+#define fGEN_TCG_L2_loadbsw2_pci(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pci, true)
+#define fGEN_TCG_L2_loadbzw2_pcr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pcr(1), false)
+#define fGEN_TCG_L2_loadbsw2_pcr(SHORTCODE) \
+ fGEN_TCG_loadbXw2(GET_EA_pcr(1), true)
+
+/*
+ * These instructions load 4 bytes and places them in
+ * four halves of the destination register pair.
+ * The GET_EA macro determines the addressing mode.
+ * The SIGN argument determines whether to zero-extend or
+ * sign-extend.
+ */
+#define fGEN_TCG_loadbXw4(GET_EA, SIGN) \
+ do { \
+ TCGv tmp = tcg_temp_new(); \
+ TCGv byte = tcg_temp_new(); \
+ GET_EA; \
+ fLOAD(1, 4, u, EA, tmp); \
+ tcg_gen_movi_i64(RddV, 0); \
+ for (int i = 0; i < 4; i++) { \
+ gen_set_half_i64(i, RddV, gen_get_byte(byte, i, tmp, (SIGN))); \
+ } \
+ tcg_temp_free(tmp); \
+ tcg_temp_free(byte); \
+ } while (0)
+
+#define fGEN_TCG_L2_loadbzw4_io(SHORTCODE) \
+ fGEN_TCG_loadbXw4(fEA_RI(RsV, siV), false)
+#define fGEN_TCG_L4_loadbzw4_ur(SHORTCODE) \
+ fGEN_TCG_loadbXw4(fEA_IRs(UiV, RtV, uiV), false)
+#define fGEN_TCG_L2_loadbsw4_io(SHORTCODE) \
+ fGEN_TCG_loadbXw4(fEA_RI(RsV, siV), true)
+#define fGEN_TCG_L4_loadbsw4_ur(SHORTCODE) \
+ fGEN_TCG_loadbXw4(fEA_IRs(UiV, RtV, uiV), true)
+#define fGEN_TCG_L2_loadbzw4_pci(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pci, false)
+#define fGEN_TCG_L2_loadbsw4_pci(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pci, true)
+#define fGEN_TCG_L2_loadbzw4_pcr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pcr(2), false)
+#define fGEN_TCG_L2_loadbsw4_pcr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pcr(2), true)
+#define fGEN_TCG_L4_loadbzw4_ap(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_ap, false)
+#define fGEN_TCG_L2_loadbzw4_pr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pr, false)
+#define fGEN_TCG_L2_loadbzw4_pbr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pbr, false)
+#define fGEN_TCG_L2_loadbzw4_pi(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pi, false)
+#define fGEN_TCG_L4_loadbsw4_ap(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_ap, true)
+#define fGEN_TCG_L2_loadbsw4_pr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pr, true)
+#define fGEN_TCG_L2_loadbsw4_pbr(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pbr, true)
+#define fGEN_TCG_L2_loadbsw4_pi(SHORTCODE) \
+ fGEN_TCG_loadbXw4(GET_EA_pi, true)
+
+/*
+ * These instructions load a half word, shift the destination right by 16 bits
+ * and place the loaded value in the high half word of the destination pair.
+ * The GET_EA macro determines the addressing mode.
+ */
+#define fGEN_TCG_loadalignh(GET_EA) \
+ do { \
+ TCGv tmp = tcg_temp_new(); \
+ TCGv_i64 tmp_i64 = tcg_temp_new_i64(); \
+ GET_EA; \
+ fLOAD(1, 2, u, EA, tmp); \
+ tcg_gen_extu_i32_i64(tmp_i64, tmp); \
+ tcg_gen_shri_i64(RyyV, RyyV, 16); \
+ tcg_gen_deposit_i64(RyyV, RyyV, tmp_i64, 48, 16); \
+ tcg_temp_free(tmp); \
+ tcg_temp_free_i64(tmp_i64); \
+ } while (0)
+
+#define fGEN_TCG_L4_loadalignh_ur(SHORTCODE) \
+ fGEN_TCG_loadalignh(fEA_IRs(UiV, RtV, uiV))
+#define fGEN_TCG_L2_loadalignh_io(SHORTCODE) \
+ fGEN_TCG_loadalignh(fEA_RI(RsV, siV))
+#define fGEN_TCG_L2_loadalignh_pci(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_pci)
+#define fGEN_TCG_L2_loadalignh_pcr(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_pcr(1))
+#define fGEN_TCG_L4_loadalignh_ap(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_ap)
+#define fGEN_TCG_L2_loadalignh_pr(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_pr)
+#define fGEN_TCG_L2_loadalignh_pbr(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_pbr)
+#define fGEN_TCG_L2_loadalignh_pi(SHORTCODE) \
+ fGEN_TCG_loadalignh(GET_EA_pi)
+
+/* Same as above, but loads a byte instead of half word */
+#define fGEN_TCG_loadalignb(GET_EA) \
+ do { \
+ TCGv tmp = tcg_temp_new(); \
+ TCGv_i64 tmp_i64 = tcg_temp_new_i64(); \
+ GET_EA; \
+ fLOAD(1, 1, u, EA, tmp); \
+ tcg_gen_extu_i32_i64(tmp_i64, tmp); \
+ tcg_gen_shri_i64(RyyV, RyyV, 8); \
+ tcg_gen_deposit_i64(RyyV, RyyV, tmp_i64, 56, 8); \
+ tcg_temp_free(tmp); \
+ tcg_temp_free_i64(tmp_i64); \
+ } while (0)
+
+#define fGEN_TCG_L2_loadalignb_io(SHORTCODE) \
+ fGEN_TCG_loadalignb(fEA_RI(RsV, siV))
+#define fGEN_TCG_L4_loadalignb_ur(SHORTCODE) \
+ fGEN_TCG_loadalignb(fEA_IRs(UiV, RtV, uiV))
+#define fGEN_TCG_L2_loadalignb_pci(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_pci)
+#define fGEN_TCG_L2_loadalignb_pcr(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_pcr(0))
+#define fGEN_TCG_L4_loadalignb_ap(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_ap)
+#define fGEN_TCG_L2_loadalignb_pr(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_pr)
+#define fGEN_TCG_L2_loadalignb_pbr(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_pbr)
+#define fGEN_TCG_L2_loadalignb_pi(SHORTCODE) \
+ fGEN_TCG_loadalignb(GET_EA_pi)
+
+/*
* Predicated loads
* Here is a primer to understand the tag names
*
@@ -195,6 +428,191 @@
#define fGEN_TCG_S4_stored_locked(SHORTCODE) \
do { SHORTCODE; READ_PREG(PdV, PdN); } while (0)
+#define fGEN_TCG_STORE(SHORTCODE) \
+ do { \
+ TCGv HALF = tcg_temp_new(); \
+ TCGv BYTE = tcg_temp_new(); \
+ SHORTCODE; \
+ tcg_temp_free(HALF); \
+ tcg_temp_free(BYTE); \
+ } while (0)
+
+#define fGEN_TCG_STORE_pcr(SHIFT, STORE) \
+ do { \
+ TCGv ireg = tcg_temp_new(); \
+ TCGv HALF = tcg_temp_new(); \
+ TCGv BYTE = tcg_temp_new(); \
+ tcg_gen_mov_tl(EA, RxV); \
+ gen_read_ireg(ireg, MuV, SHIFT); \
+ gen_helper_fcircadd(RxV, RxV, ireg, MuV, hex_gpr[HEX_REG_CS0 + MuN]); \
+ STORE; \
+ tcg_temp_free(ireg); \
+ tcg_temp_free(HALF); \
+ tcg_temp_free(BYTE); \
+ } while (0)
+
+#define fGEN_TCG_S2_storerb_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerb_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerb_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(0, fSTORE(1, 1, EA, fGETBYTE(0, RtV)))
+
+#define fGEN_TCG_S2_storerh_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerh_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerh_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(1, fSTORE(1, 2, EA, fGETHALF(0, RtV)))
+
+#define fGEN_TCG_S2_storerf_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerf_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerf_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(1, fSTORE(1, 2, EA, fGETHALF(1, RtV)))
+
+#define fGEN_TCG_S2_storeri_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storeri_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storeri_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(2, fSTORE(1, 4, EA, RtV))
+
+#define fGEN_TCG_S2_storerd_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerd_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerd_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(3, fSTORE(1, 8, EA, RttV))
+
+#define fGEN_TCG_S2_storerbnew_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerbnew_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerbnew_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(0, fSTORE(1, 1, EA, fGETBYTE(0, NtN)))
+
+#define fGEN_TCG_S2_storerhnew_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerhnew_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerhnew_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(1, fSTORE(1, 2, EA, fGETHALF(0, NtN)))
+
+#define fGEN_TCG_S2_storerinew_pbr(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerinew_pci(SHORTCODE) \
+ fGEN_TCG_STORE(SHORTCODE)
+#define fGEN_TCG_S2_storerinew_pcr(SHORTCODE) \
+ fGEN_TCG_STORE_pcr(2, fSTORE(1, 4, EA, NtN))
+
+/*
+ * Mathematical operations with more than one definition require
+ * special handling
+ */
+#define fGEN_TCG_A5_ACS(SHORTCODE) \
+ do { \
+ gen_helper_vacsh_pred(PeV, cpu_env, RxxV, RssV, RttV); \
+ gen_helper_vacsh_val(RxxV, cpu_env, RxxV, RssV, RttV); \
+ } while (0)
+
+/*
+ * Approximate reciprocal
+ * r3,p1 = sfrecipa(r0, r1)
+ *
+ * The helper packs the 2 32-bit results into a 64-bit value,
+ * so unpack them into the proper results.
+ */
+#define fGEN_TCG_F2_sfrecipa(SHORTCODE) \
+ do { \
+ TCGv_i64 tmp = tcg_temp_new_i64(); \
+ gen_helper_sfrecipa(tmp, cpu_env, RsV, RtV); \
+ tcg_gen_extrh_i64_i32(RdV, tmp); \
+ tcg_gen_extrl_i64_i32(PeV, tmp); \
+ tcg_temp_free_i64(tmp); \
+ } while (0)
+
+/*
+ * Approximation of the reciprocal square root
+ * r1,p0 = sfinvsqrta(r0)
+ *
+ * The helper packs the 2 32-bit results into a 64-bit value,
+ * so unpack them into the proper results.
+ */
+#define fGEN_TCG_F2_sfinvsqrta(SHORTCODE) \
+ do { \
+ TCGv_i64 tmp = tcg_temp_new_i64(); \
+ gen_helper_sfinvsqrta(tmp, cpu_env, RsV); \
+ tcg_gen_extrh_i64_i32(RdV, tmp); \
+ tcg_gen_extrl_i64_i32(PeV, tmp); \
+ tcg_temp_free_i64(tmp); \
+ } while (0)
+
+/*
+ * Add or subtract with carry.
+ * Predicate register is used as an extra input and output.
+ * r5:4 = add(r1:0, r3:2, p1):carry
+ */
+#define fGEN_TCG_A4_addp_c(SHORTCODE) \
+ do { \
+ TCGv_i64 carry = tcg_temp_new_i64(); \
+ TCGv_i64 zero = tcg_const_i64(0); \
+ tcg_gen_extu_i32_i64(carry, PxV); \
+ tcg_gen_andi_i64(carry, carry, 1); \
+ tcg_gen_add2_i64(RddV, carry, RssV, zero, carry, zero); \
+ tcg_gen_add2_i64(RddV, carry, RddV, carry, RttV, zero); \
+ tcg_gen_extrl_i64_i32(PxV, carry); \
+ gen_8bitsof(PxV, PxV); \
+ tcg_temp_free_i64(carry); \
+ tcg_temp_free_i64(zero); \
+ } while (0)
+
+/* r5:4 = sub(r1:0, r3:2, p1):carry */
+#define fGEN_TCG_A4_subp_c(SHORTCODE) \
+ do { \
+ TCGv_i64 carry = tcg_temp_new_i64(); \
+ TCGv_i64 zero = tcg_const_i64(0); \
+ TCGv_i64 not_RttV = tcg_temp_new_i64(); \
+ tcg_gen_extu_i32_i64(carry, PxV); \
+ tcg_gen_andi_i64(carry, carry, 1); \
+ tcg_gen_not_i64(not_RttV, RttV); \
+ tcg_gen_add2_i64(RddV, carry, RssV, zero, carry, zero); \
+ tcg_gen_add2_i64(RddV, carry, RddV, carry, not_RttV, zero); \
+ tcg_gen_extrl_i64_i32(PxV, carry); \
+ gen_8bitsof(PxV, PxV); \
+ tcg_temp_free_i64(carry); \
+ tcg_temp_free_i64(zero); \
+ tcg_temp_free_i64(not_RttV); \
+ } while (0)
+
+/*
+ * Compare each of the 8 unsigned bytes
+ * The minimum is placed in each byte of the destination.
+ * Each bit of the predicate is set true if the bit from the first operand
+ * is greater than the bit from the second operand.
+ * r5:4,p1 = vminub(r1:0, r3:2)
+ */
+#define fGEN_TCG_A6_vminub_RdP(SHORTCODE) \
+ do { \
+ TCGv left = tcg_temp_new(); \
+ TCGv right = tcg_temp_new(); \
+ TCGv tmp = tcg_temp_new(); \
+ tcg_gen_movi_tl(PeV, 0); \
+ tcg_gen_movi_i64(RddV, 0); \
+ for (int i = 0; i < 8; i++) { \
+ gen_get_byte_i64(left, i, RttV, false); \
+ gen_get_byte_i64(right, i, RssV, false); \
+ tcg_gen_setcond_tl(TCG_COND_GT, tmp, left, right); \
+ tcg_gen_deposit_tl(PeV, PeV, tmp, i, 1); \
+ tcg_gen_umin_tl(tmp, left, right); \
+ gen_set_byte_i64(i, RddV, tmp); \
+ } \
+ tcg_temp_free(left); \
+ tcg_temp_free(right); \
+ tcg_temp_free(tmp); \
+ } while (0)
+
/* Floating point */
#define fGEN_TCG_F2_conv_sf2df(SHORTCODE) \
gen_helper_conv_sf2df(RddV, cpu_env, RsV)
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py
index db9f663a77..7ceb25b5f6 100755
--- a/target/hexagon/gen_tcg_funcs.py
+++ b/target/hexagon/gen_tcg_funcs.py
@@ -316,7 +316,7 @@ def genptr_dst_write(f, tag, regtype, regid):
print("Bad register parse: ", regtype, regid)
elif (regtype == "P"):
if (regid in {"d", "e", "x"}):
- f.write(" gen_log_pred_write(%s%sN, %s%sV);\n" % \
+ f.write(" gen_log_pred_write(ctx, %s%sN, %s%sV);\n" % \
(regtype, regid, regtype, regid))
f.write(" ctx_log_pred_write(ctx, %s%sN);\n" % \
(regtype, regid))
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c
index 7481f4c1dd..f93f8953ff 100644
--- a/target/hexagon/genptr.c
+++ b/target/hexagon/genptr.c
@@ -15,7 +15,6 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#define QEMU_GENERATE
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "cpu.h"
@@ -24,7 +23,9 @@
#include "insn.h"
#include "opcodes.h"
#include "translate.h"
+#define QEMU_GENERATE /* Used internally by macros.h */
#include "macros.h"
+#undef QEMU_GENERATE
#include "gen_tcg.h"
static inline TCGv gen_read_preg(TCGv pred, uint8_t num)
@@ -35,20 +36,24 @@ static inline TCGv gen_read_preg(TCGv pred, uint8_t num)
static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot)
{
- TCGv one = tcg_const_tl(1);
TCGv zero = tcg_const_tl(0);
TCGv slot_mask = tcg_temp_new();
tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot);
tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum], slot_mask, zero,
val, hex_new_value[rnum]);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_reg_written[rnum], slot_mask, zero,
- one, hex_reg_written[rnum]);
-#endif
+ if (HEX_DEBUG) {
+ /*
+ * Do this so HELPER(debug_commit_end) will know
+ *
+ * Note that slot_mask indicates the value is not written
+ * (i.e., slot was cancelled), so we create a true/false value before
+ * or'ing with hex_reg_written[rnum].
+ */
+ tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero);
+ tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask);
+ }
- tcg_temp_free(one);
tcg_temp_free(zero);
tcg_temp_free(slot_mask);
}
@@ -56,45 +61,44 @@ static inline void gen_log_predicated_reg_write(int rnum, TCGv val, int slot)
static inline void gen_log_reg_write(int rnum, TCGv val)
{
tcg_gen_mov_tl(hex_new_value[rnum], val);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movi_tl(hex_reg_written[rnum], 1);
-#endif
+ if (HEX_DEBUG) {
+ /* Do this so HELPER(debug_commit_end) will know */
+ tcg_gen_movi_tl(hex_reg_written[rnum], 1);
+ }
}
static void gen_log_predicated_reg_write_pair(int rnum, TCGv_i64 val, int slot)
{
TCGv val32 = tcg_temp_new();
- TCGv one = tcg_const_tl(1);
TCGv zero = tcg_const_tl(0);
TCGv slot_mask = tcg_temp_new();
tcg_gen_andi_tl(slot_mask, hex_slot_cancelled, 1 << slot);
/* Low word */
tcg_gen_extrl_i64_i32(val32, val);
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum], slot_mask, zero,
- val32, hex_new_value[rnum]);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_reg_written[rnum],
+ tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum],
slot_mask, zero,
- one, hex_reg_written[rnum]);
-#endif
-
+ val32, hex_new_value[rnum]);
/* High word */
tcg_gen_extrh_i64_i32(val32, val);
tcg_gen_movcond_tl(TCG_COND_EQ, hex_new_value[rnum + 1],
slot_mask, zero,
val32, hex_new_value[rnum + 1]);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movcond_tl(TCG_COND_EQ, hex_reg_written[rnum + 1],
- slot_mask, zero,
- one, hex_reg_written[rnum + 1]);
-#endif
+ if (HEX_DEBUG) {
+ /*
+ * Do this so HELPER(debug_commit_end) will know
+ *
+ * Note that slot_mask indicates the value is not written
+ * (i.e., slot was cancelled), so we create a true/false value before
+ * or'ing with hex_reg_written[rnum].
+ */
+ tcg_gen_setcond_tl(TCG_COND_EQ, slot_mask, slot_mask, zero);
+ tcg_gen_or_tl(hex_reg_written[rnum], hex_reg_written[rnum], slot_mask);
+ tcg_gen_or_tl(hex_reg_written[rnum + 1], hex_reg_written[rnum + 1],
+ slot_mask);
+ }
tcg_temp_free(val32);
- tcg_temp_free(one);
tcg_temp_free(zero);
tcg_temp_free(slot_mask);
}
@@ -103,33 +107,41 @@ static void gen_log_reg_write_pair(int rnum, TCGv_i64 val)
{
/* Low word */
tcg_gen_extrl_i64_i32(hex_new_value[rnum], val);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movi_tl(hex_reg_written[rnum], 1);
-#endif
+ if (HEX_DEBUG) {
+ /* Do this so HELPER(debug_commit_end) will know */
+ tcg_gen_movi_tl(hex_reg_written[rnum], 1);
+ }
/* High word */
tcg_gen_extrh_i64_i32(hex_new_value[rnum + 1], val);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_movi_tl(hex_reg_written[rnum + 1], 1);
-#endif
+ if (HEX_DEBUG) {
+ /* Do this so HELPER(debug_commit_end) will know */
+ tcg_gen_movi_tl(hex_reg_written[rnum + 1], 1);
+ }
}
-static inline void gen_log_pred_write(int pnum, TCGv val)
+static inline void gen_log_pred_write(DisasContext *ctx, int pnum, TCGv val)
{
TCGv zero = tcg_const_tl(0);
TCGv base_val = tcg_temp_new();
TCGv and_val = tcg_temp_new();
TCGv pred_written = tcg_temp_new();
- /* Multiple writes to the same preg are and'ed together */
tcg_gen_andi_tl(base_val, val, 0xff);
- tcg_gen_and_tl(and_val, base_val, hex_new_pred_value[pnum]);
- tcg_gen_andi_tl(pred_written, hex_pred_written, 1 << pnum);
- tcg_gen_movcond_tl(TCG_COND_NE, hex_new_pred_value[pnum],
- pred_written, zero,
- and_val, base_val);
+
+ /*
+ * Section 6.1.3 of the Hexagon V67 Programmer's Reference Manual
+ *
+ * Multiple writes to the same preg are and'ed together
+ * If this is the first predicate write in the packet, do a
+ * straight assignment. Otherwise, do an and.
+ */
+ if (!test_bit(pnum, ctx->pregs_written)) {
+ tcg_gen_mov_tl(hex_new_pred_value[pnum], base_val);
+ } else {
+ tcg_gen_and_tl(hex_new_pred_value[pnum],
+ hex_new_pred_value[pnum], base_val);
+ }
tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << pnum);
tcg_temp_free(zero);
@@ -254,6 +266,61 @@ static inline void gen_write_ctrl_reg_pair(DisasContext *ctx, int reg_num,
}
}
+static TCGv gen_get_byte(TCGv result, int N, TCGv src, bool sign)
+{
+ if (sign) {
+ tcg_gen_sextract_tl(result, src, N * 8, 8);
+ } else {
+ tcg_gen_extract_tl(result, src, N * 8, 8);
+ }
+ return result;
+}
+
+static TCGv gen_get_byte_i64(TCGv result, int N, TCGv_i64 src, bool sign)
+{
+ TCGv_i64 res64 = tcg_temp_new_i64();
+ if (sign) {
+ tcg_gen_sextract_i64(res64, src, N * 8, 8);
+ } else {
+ tcg_gen_extract_i64(res64, src, N * 8, 8);
+ }
+ tcg_gen_extrl_i64_i32(result, res64);
+ tcg_temp_free_i64(res64);
+
+ return result;
+}
+
+static inline TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign)
+{
+ if (sign) {
+ tcg_gen_sextract_tl(result, src, N * 16, 16);
+ } else {
+ tcg_gen_extract_tl(result, src, N * 16, 16);
+ }
+ return result;
+}
+
+static inline void gen_set_half(int N, TCGv result, TCGv src)
+{
+ tcg_gen_deposit_tl(result, result, src, N * 16, 16);
+}
+
+static inline void gen_set_half_i64(int N, TCGv_i64 result, TCGv src)
+{
+ TCGv_i64 src64 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(src64, src);
+ tcg_gen_deposit_i64(result, result, src64, N * 16, 16);
+ tcg_temp_free_i64(src64);
+}
+
+static void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src)
+{
+ TCGv_i64 src64 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(src64, src);
+ tcg_gen_deposit_i64(result, result, src64, N * 8, 8);
+ tcg_temp_free_i64(src64);
+}
+
static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index)
{
tcg_gen_qemu_ld32u(dest, vaddr, mem_index);
@@ -327,5 +394,85 @@ static inline void gen_store_conditional8(CPUHexagonState *env,
tcg_gen_movi_tl(hex_llsc_addr, ~0);
}
+static inline void gen_store32(TCGv vaddr, TCGv src, int width, int slot)
+{
+ tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
+ tcg_gen_movi_tl(hex_store_width[slot], width);
+ tcg_gen_mov_tl(hex_store_val32[slot], src);
+}
+
+static inline void gen_store1(TCGv_env cpu_env, TCGv vaddr, TCGv src,
+ DisasContext *ctx, int slot)
+{
+ gen_store32(vaddr, src, 1, slot);
+ ctx->store_width[slot] = 1;
+}
+
+static inline void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src,
+ DisasContext *ctx, int slot)
+{
+ TCGv tmp = tcg_const_tl(src);
+ gen_store1(cpu_env, vaddr, tmp, ctx, slot);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src,
+ DisasContext *ctx, int slot)
+{
+ gen_store32(vaddr, src, 2, slot);
+ ctx->store_width[slot] = 2;
+}
+
+static inline void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src,
+ DisasContext *ctx, int slot)
+{
+ TCGv tmp = tcg_const_tl(src);
+ gen_store2(cpu_env, vaddr, tmp, ctx, slot);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src,
+ DisasContext *ctx, int slot)
+{
+ gen_store32(vaddr, src, 4, slot);
+ ctx->store_width[slot] = 4;
+}
+
+static inline void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src,
+ DisasContext *ctx, int slot)
+{
+ TCGv tmp = tcg_const_tl(src);
+ gen_store4(cpu_env, vaddr, tmp, ctx, slot);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src,
+ DisasContext *ctx, int slot)
+{
+ tcg_gen_mov_tl(hex_store_addr[slot], vaddr);
+ tcg_gen_movi_tl(hex_store_width[slot], 8);
+ tcg_gen_mov_i64(hex_store_val64[slot], src);
+ ctx->store_width[slot] = 8;
+}
+
+static inline void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src,
+ DisasContext *ctx, int slot)
+{
+ TCGv_i64 tmp = tcg_const_i64(src);
+ gen_store8(cpu_env, vaddr, tmp, ctx, slot);
+ tcg_temp_free_i64(tmp);
+}
+
+static TCGv gen_8bitsof(TCGv result, TCGv value)
+{
+ TCGv zero = tcg_const_tl(0);
+ TCGv ones = tcg_const_tl(0xff);
+ tcg_gen_movcond_tl(TCG_COND_NE, result, value, zero, ones, zero);
+ tcg_temp_free(zero);
+ tcg_temp_free(ones);
+
+ return result;
+}
+
#include "tcg_funcs_generated.c.inc"
#include "tcg_func_table_generated.c.inc"
diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h
index a5f340ce67..ca201fb680 100644
--- a/target/hexagon/helper.h
+++ b/target/hexagon/helper.h
@@ -19,13 +19,16 @@
#include "helper_protos_generated.h.inc"
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32)
-#if HEX_DEBUG
DEF_HELPER_1(debug_start_packet, void, env)
DEF_HELPER_FLAGS_3(debug_check_store_width, TCG_CALL_NO_WG, void, env, int, int)
DEF_HELPER_FLAGS_3(debug_commit_end, TCG_CALL_NO_WG, void, env, int, int)
-#endif
DEF_HELPER_2(commit_store, void, env, int)
DEF_HELPER_FLAGS_4(fcircadd, TCG_CALL_NO_RWG_SE, s32, s32, s32, s32, s32)
+DEF_HELPER_FLAGS_1(fbrev, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_3(sfrecipa, i64, env, f32, f32)
+DEF_HELPER_2(sfinvsqrta, i64, env, f32)
+DEF_HELPER_4(vacsh_val, s64, env, s64, s64, s64)
+DEF_HELPER_FLAGS_4(vacsh_pred, TCG_CALL_NO_RWG_SE, s32, env, s64, s64, s64)
/* Floating point */
DEF_HELPER_2(conv_sf2df, f64, env, f32)
@@ -38,21 +41,21 @@ DEF_HELPER_2(conv_ud2sf, f32, env, s64)
DEF_HELPER_2(conv_ud2df, f64, env, s64)
DEF_HELPER_2(conv_d2sf, f32, env, s64)
DEF_HELPER_2(conv_d2df, f64, env, s64)
-DEF_HELPER_2(conv_sf2uw, s32, env, f32)
+DEF_HELPER_2(conv_sf2uw, i32, env, f32)
DEF_HELPER_2(conv_sf2w, s32, env, f32)
-DEF_HELPER_2(conv_sf2ud, s64, env, f32)
+DEF_HELPER_2(conv_sf2ud, i64, env, f32)
DEF_HELPER_2(conv_sf2d, s64, env, f32)
-DEF_HELPER_2(conv_df2uw, s32, env, f64)
+DEF_HELPER_2(conv_df2uw, i32, env, f64)
DEF_HELPER_2(conv_df2w, s32, env, f64)
-DEF_HELPER_2(conv_df2ud, s64, env, f64)
+DEF_HELPER_2(conv_df2ud, i64, env, f64)
DEF_HELPER_2(conv_df2d, s64, env, f64)
-DEF_HELPER_2(conv_sf2uw_chop, s32, env, f32)
+DEF_HELPER_2(conv_sf2uw_chop, i32, env, f32)
DEF_HELPER_2(conv_sf2w_chop, s32, env, f32)
-DEF_HELPER_2(conv_sf2ud_chop, s64, env, f32)
+DEF_HELPER_2(conv_sf2ud_chop, i64, env, f32)
DEF_HELPER_2(conv_sf2d_chop, s64, env, f32)
-DEF_HELPER_2(conv_df2uw_chop, s32, env, f64)
+DEF_HELPER_2(conv_df2uw_chop, i32, env, f64)
DEF_HELPER_2(conv_df2w_chop, s32, env, f64)
-DEF_HELPER_2(conv_df2ud_chop, s64, env, f64)
+DEF_HELPER_2(conv_df2ud_chop, i64, env, f64)
DEF_HELPER_2(conv_df2d_chop, s64, env, f64)
DEF_HELPER_3(sfadd, f32, env, f32, f32)
DEF_HELPER_3(sfsub, f32, env, f32, f32)
diff --git a/target/hexagon/iclass.c b/target/hexagon/iclass.c
index 378d8a6a75..6091286993 100644
--- a/target/hexagon/iclass.c
+++ b/target/hexagon/iclass.c
@@ -53,10 +53,6 @@ SlotMask find_iclass_slots(Opcode opcode, int itype)
(opcode == Y2_isync) ||
(opcode == J2_pause) || (opcode == J4_hintjumpr)) {
return SLOTS_2;
- } else if ((itype == ICLASS_V2LDST) && (GET_ATTRIB(opcode, A_STORE))) {
- return SLOTS_01;
- } else if ((itype == ICLASS_V2LDST) && (!GET_ATTRIB(opcode, A_STORE))) {
- return SLOTS_01;
} else if (GET_ATTRIB(opcode, A_CRSLOT23)) {
return SLOTS_23;
} else if (GET_ATTRIB(opcode, A_RESTRICT_PREFERSLOT0)) {
diff --git a/target/hexagon/imported/alu.idef b/target/hexagon/imported/alu.idef
index 45cc529fbc..58477ae40a 100644
--- a/target/hexagon/imported/alu.idef
+++ b/target/hexagon/imported/alu.idef
@@ -153,6 +153,21 @@ Q6INSN(A2_subp,"Rdd32=sub(Rtt32,Rss32)",ATTRIBS(),
"Sub",
{ RddV=RttV-RssV;})
+/* 64-bit with carry */
+
+Q6INSN(A4_addp_c,"Rdd32=add(Rss32,Rtt32,Px4):carry",ATTRIBS(),"Add with Carry",
+{
+ RddV = RssV + RttV + fLSBOLD(PxV);
+ PxV = f8BITSOF(fCARRY_FROM_ADD(RssV,RttV,fLSBOLD(PxV)));
+})
+
+Q6INSN(A4_subp_c,"Rdd32=sub(Rss32,Rtt32,Px4):carry",ATTRIBS(),"Sub with Carry",
+{
+ RddV = RssV + ~RttV + fLSBOLD(PxV);
+ PxV = f8BITSOF(fCARRY_FROM_ADD(RssV,~RttV,fLSBOLD(PxV)));
+})
+
+
/* NEG and ABS */
Q6INSN(A2_negsat,"Rd32=neg(Rs32):sat",ATTRIBS(),
@@ -1240,6 +1255,35 @@ MINMAX(uw,WORD,UWORD,2)
#undef VMINORMAX3
+Q6INSN(A5_ACS,"Rxx32,Pe4=vacsh(Rss32,Rtt32)",ATTRIBS(),
+"Add Compare and Select elements of two vectors, record the maximums and the decisions ",
+{
+ fHIDE(int i;)
+ fHIDE(int xv;)
+ fHIDE(int sv;)
+ fHIDE(int tv;)
+ for (i = 0; i < 4; i++) {
+ xv = (int) fGETHALF(i,RxxV);
+ sv = (int) fGETHALF(i,RssV);
+ tv = (int) fGETHALF(i,RttV);
+ xv = xv + tv; //assumes 17bit datapath
+ sv = sv - tv; //assumes 17bit datapath
+ fSETBIT(i*2, PeV, (xv > sv));
+ fSETBIT(i*2+1,PeV, (xv > sv));
+ fSETHALF(i, RxxV, fSATH(fMAX(xv,sv)));
+ }
+})
+
+Q6INSN(A6_vminub_RdP,"Rdd32,Pe4=vminub(Rtt32,Rss32)",ATTRIBS(),
+"Vector minimum of bytes, records minimum and decision vector",
+{
+ fHIDE(int i;)
+ for (i = 0; i < 8; i++) {
+ fSETBIT(i, PeV, (fGETUBYTE(i,RttV) > fGETUBYTE(i,RssV)));
+ fSETBYTE(i,RddV,fMIN(fGETUBYTE(i,RttV),fGETUBYTE(i,RssV)));
+ }
+})
+
/**********************************************/
/* Vector Min/Max */
/**********************************************/
diff --git a/target/hexagon/imported/compare.idef b/target/hexagon/imported/compare.idef
index 3551467854..abd016ffb5 100644
--- a/target/hexagon/imported/compare.idef
+++ b/target/hexagon/imported/compare.idef
@@ -198,11 +198,11 @@ Q6INSN(C4_or_orn,"Pd4=or(Ps4,or(Pt4,!Pu4))",ATTRIBS(A_CRSLOT23),
Q6INSN(C2_any8,"Pd4=any8(Ps4)",ATTRIBS(A_CRSLOT23),
"Logical ANY of low 8 predicate bits",
-{ PsV ? (PdV=0xff) : (PdV=0x00); })
+{ PdV = (PsV ? 0xff : 0x00); })
Q6INSN(C2_all8,"Pd4=all8(Ps4)",ATTRIBS(A_CRSLOT23),
"Logical ALL of low 8 predicate bits",
-{ (PsV==0xff) ? (PdV=0xff) : (PdV=0x00); })
+{ PdV = (PsV == 0xff ? 0xff : 0x00); })
Q6INSN(C2_vitpack,"Rd32=vitpack(Ps4,Pt4)",ATTRIBS(),
"Pack the odd and even bits of two predicate registers",
@@ -212,7 +212,7 @@ Q6INSN(C2_vitpack,"Rd32=vitpack(Ps4,Pt4)",ATTRIBS(),
Q6INSN(C2_mux,"Rd32=mux(Pu4,Rs32,Rt32)",ATTRIBS(),
"Scalar MUX",
-{ (fLSBOLD(PuV)) ? (RdV=RsV):(RdV=RtV); })
+{ RdV = (fLSBOLD(PuV) ? RsV : RtV); })
Q6INSN(C2_cmovenewit,"if (Pu4.new) Rd32=#s12",ATTRIBS(A_ARCHV2),
@@ -269,18 +269,18 @@ Q6INSN(C2_ccombinewf,"if (!Pu4) Rdd32=combine(Rs32,Rt32)",ATTRIBS(A_ARCHV2),
Q6INSN(C2_muxii,"Rd32=mux(Pu4,#s8,#S8)",ATTRIBS(A_ARCHV2),
"Scalar MUX immediates",
-{ fIMMEXT(siV); (fLSBOLD(PuV)) ? (RdV=siV):(RdV=SiV); })
+{ fIMMEXT(siV); RdV = (fLSBOLD(PuV) ? siV : SiV); })
Q6INSN(C2_muxir,"Rd32=mux(Pu4,Rs32,#s8)",ATTRIBS(A_ARCHV2),
"Scalar MUX register immediate",
-{ fIMMEXT(siV); (fLSBOLD(PuV)) ? (RdV=RsV):(RdV=siV); })
+{ fIMMEXT(siV); RdV = (fLSBOLD(PuV) ? RsV : siV); })
Q6INSN(C2_muxri,"Rd32=mux(Pu4,#s8,Rs32)",ATTRIBS(A_ARCHV2),
"Scalar MUX register immediate",
-{ fIMMEXT(siV); (fLSBOLD(PuV)) ? (RdV=siV):(RdV=RsV); })
+{ fIMMEXT(siV); RdV = (fLSBOLD(PuV) ? siV : RsV); })
diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def
index c21cb730af..35ae3d2369 100644
--- a/target/hexagon/imported/encode_pp.def
+++ b/target/hexagon/imported/encode_pp.def
@@ -294,12 +294,14 @@ DEF_CLASS32(ICLASS_LD" ---- -------- PP------ --------",LD)
DEF_CLASS32(ICLASS_LD" 0--- -------- PP------ --------",LD_ADDR_ROFFSET)
+DEF_CLASS32(ICLASS_LD" 100- -------- PP----0- --------",LD_ADDR_POST_CIRC_IMMED)
DEF_CLASS32(ICLASS_LD" 101- -------- PP00---- --------",LD_ADDR_POST_IMMED)
DEF_CLASS32(ICLASS_LD" 101- -------- PP01---- --------",LD_ADDR_ABS_UPDATE_V4)
DEF_CLASS32(ICLASS_LD" 101- -------- PP1----- --------",LD_ADDR_POST_IMMED_PRED_V2)
DEF_CLASS32(ICLASS_LD" 110- -------- PP-0---- 0-------",LD_ADDR_POST_REG)
DEF_CLASS32(ICLASS_LD" 110- -------- PP-1---- --------",LD_ADDR_ABS_PLUS_REG_V4)
DEF_CLASS32(ICLASS_LD" 100- -------- PP----1- --------",LD_ADDR_POST_CREG_V2)
+DEF_CLASS32(ICLASS_LD" 111- -------- PP------ 0-------",LD_ADDR_POST_BREV_REG)
DEF_CLASS32(ICLASS_LD" 111- -------- PP------ 1-------",LD_ADDR_PRED_ABS_V4)
DEF_FIELD32(ICLASS_LD" !!!- -------- PP------ --------",LD_Amode,"Amode")
@@ -308,18 +310,24 @@ DEF_FIELD32(ICLASS_LD" ---- --!----- PP------ --------",LD_UN,"Unsigned")
#define STD_LD_ENC(TAG,OPC) \
DEF_ENC32(L2_load##TAG##_io, ICLASS_LD" 0 ii "OPC" sssss PPiiiiii iiiddddd")\
+DEF_ENC32(L2_load##TAG##_pci, ICLASS_LD" 1 00 "OPC" xxxxx PPu0--0i iiiddddd")\
DEF_ENC32(L2_load##TAG##_pi, ICLASS_LD" 1 01 "OPC" xxxxx PP00---i iiiddddd")\
DEF_ENC32(L4_load##TAG##_ap, ICLASS_LD" 1 01 "OPC" eeeee PP01IIII -IIddddd")\
DEF_ENC32(L2_load##TAG##_pr, ICLASS_LD" 1 10 "OPC" xxxxx PPu0---- 0--ddddd")\
DEF_ENC32(L4_load##TAG##_ur, ICLASS_LD" 1 10 "OPC" ttttt PPi1IIII iIIddddd")\
+DEF_ENC32(L2_load##TAG##_pcr, ICLASS_LD" 1 00 "OPC" xxxxx PPu0--1- 0--ddddd")\
+DEF_ENC32(L2_load##TAG##_pbr, ICLASS_LD" 1 11 "OPC" xxxxx PPu0---- 0--ddddd")
#define STD_LDX_ENC(TAG,OPC) \
DEF_ENC32(L2_load##TAG##_io, ICLASS_LD" 0 ii "OPC" sssss PPiiiiii iiiyyyyy")\
+DEF_ENC32(L2_load##TAG##_pci, ICLASS_LD" 1 00 "OPC" xxxxx PPu0--0i iiiyyyyy")\
DEF_ENC32(L2_load##TAG##_pi, ICLASS_LD" 1 01 "OPC" xxxxx PP00---i iiiyyyyy")\
DEF_ENC32(L4_load##TAG##_ap, ICLASS_LD" 1 01 "OPC" eeeee PP01IIII -IIyyyyy")\
DEF_ENC32(L2_load##TAG##_pr, ICLASS_LD" 1 10 "OPC" xxxxx PPu0---- 0--yyyyy")\
DEF_ENC32(L4_load##TAG##_ur, ICLASS_LD" 1 10 "OPC" ttttt PPi1IIII iIIyyyyy")\
+DEF_ENC32(L2_load##TAG##_pcr, ICLASS_LD" 1 00 "OPC" xxxxx PPu0--1- 0--yyyyy")\
+DEF_ENC32(L2_load##TAG##_pbr, ICLASS_LD" 1 11 "OPC" xxxxx PPu0---- 0--yyyyy")
#define STD_PLD_ENC(TAG,OPC) \
@@ -334,6 +342,15 @@ DEF_ENC32(L4_pload##TAG##fnew_abs,ICLASS_LD" 1 11 "OPC" iiiii PP111tti 1--ddd
/* 0 000 misc: dealloc,loadw_locked,dcfetch */
+STD_LD_ENC(bzw4,"0 101")
+STD_LD_ENC(bzw2,"0 011")
+
+STD_LD_ENC(bsw4,"0 111")
+STD_LD_ENC(bsw2,"0 001")
+
+STD_LDX_ENC(alignh,"0 010")
+STD_LDX_ENC(alignb,"0 100")
+
STD_LD_ENC(rb, "1 000")
STD_LD_ENC(rub, "1 001")
STD_LD_ENC(rh, "1 010")
@@ -351,6 +368,7 @@ STD_PLD_ENC(rd, "1 110") /* note dest reg field LSB=0, 1 is reserved */
DEF_CLASS32( ICLASS_LD" 0--0 000----- PP------ --------",LD_MISC)
DEF_ANTICLASS32(ICLASS_LD" 0--0 000----- PP------ --------",LD_ADDR_ROFFSET)
+DEF_ANTICLASS32(ICLASS_LD" 1000 000----- PP------ --------",LD_ADDR_POST_CIRC_IMMED)
DEF_ANTICLASS32(ICLASS_LD" 1010 000----- PP------ --------",LD_ADDR_POST_IMMED)
DEF_ANTICLASS32(ICLASS_LD" 1100 000----- PP------ --------",LD_ADDR_POST_REG)
DEF_ANTICLASS32(ICLASS_LD" 1110 000----- PP------ --------",LD_ADDR_POST_REG)
@@ -397,6 +415,7 @@ DEF_FIELD32(ICLASS_ST" ---! !!------ PP------ --------",ST_Type,"Type")
DEF_FIELD32(ICLASS_ST" ---- --!----- PP------ --------",ST_UN,"Unsigned")
DEF_CLASS32(ICLASS_ST" 0--1 -------- PP------ --------",ST_ADDR_ROFFSET)
+DEF_CLASS32(ICLASS_ST" 1001 -------- PP------ ------0-",ST_ADDR_POST_CIRC_IMMED)
DEF_CLASS32(ICLASS_ST" 1011 -------- PP0----- 0-----0-",ST_ADDR_POST_IMMED)
DEF_CLASS32(ICLASS_ST" 1011 -------- PP0----- 1-------",ST_ADDR_ABS_UPDATE_V4)
DEF_CLASS32(ICLASS_ST" 1011 -------- PP1----- --------",ST_ADDR_POST_IMMED_PRED_V2)
@@ -404,6 +423,7 @@ DEF_CLASS32(ICLASS_ST" 1111 -------- PP------ 1-------",ST_ADDR_PRED_ABS_V4)
DEF_CLASS32(ICLASS_ST" 1101 -------- PP------ 0-------",ST_ADDR_POST_REG)
DEF_CLASS32(ICLASS_ST" 1101 -------- PP------ 1-------",ST_ADDR_ABS_PLUS_REG_V4)
DEF_CLASS32(ICLASS_ST" 1001 -------- PP------ ------1-",ST_ADDR_POST_CREG_V2)
+DEF_CLASS32(ICLASS_ST" 1111 -------- PP------ 0-------",ST_ADDR_POST_BREV_REG)
DEF_CLASS32(ICLASS_ST" 0--0 1------- PP------ --------",ST_MISC_STORELIKE)
DEF_CLASS32(ICLASS_ST" 1--0 0------- PP------ --------",ST_MISC_BUSOP)
DEF_CLASS32(ICLASS_ST" 0--0 0------- PP------ --------",ST_MISC_CACHEOP)
@@ -411,10 +431,13 @@ DEF_CLASS32(ICLASS_ST" 0--0 0------- PP------ --------",ST_MISC_CACHEOP)
#define STD_ST_ENC(TAG,OPC,SRC) \
DEF_ENC32(S2_store##TAG##_io, ICLASS_ST" 0 ii "OPC" sssss PPi"SRC" iiiiiiii")\
+DEF_ENC32(S2_store##TAG##_pci, ICLASS_ST" 1 00 "OPC" xxxxx PPu"SRC" 0iiii-0-")\
DEF_ENC32(S2_store##TAG##_pi, ICLASS_ST" 1 01 "OPC" xxxxx PP0"SRC" 0iiii-0-")\
DEF_ENC32(S4_store##TAG##_ap, ICLASS_ST" 1 01 "OPC" eeeee PP0"SRC" 1-IIIIII")\
DEF_ENC32(S2_store##TAG##_pr, ICLASS_ST" 1 10 "OPC" xxxxx PPu"SRC" 0-------")\
DEF_ENC32(S4_store##TAG##_ur, ICLASS_ST" 1 10 "OPC" uuuuu PPi"SRC" 1iIIIIII")\
+DEF_ENC32(S2_store##TAG##_pcr, ICLASS_ST" 1 00 "OPC" xxxxx PPu"SRC" 0-----1-")\
+DEF_ENC32(S2_store##TAG##_pbr, ICLASS_ST" 1 11 "OPC" xxxxx PPu"SRC" 0-------")
#define STD_PST_ENC(TAG,OPC,SRC) \
@@ -1017,6 +1040,8 @@ MPY_ENC(M7_dcmpyiwc_acc, "1010","xxxxx","1","0","1","0","10")
+MPY_ENC(A5_ACS, "1010","xxxxx","0","1","0","1","ee")
+MPY_ENC(A6_vminub_RdP, "1010","ddddd","0","1","1","1","ee")
/*
*/
@@ -1028,6 +1053,7 @@ MPY_ENC(F2_sfmin, "1011","ddddd","0","0","0","1","01")
MPY_ENC(F2_sfmpy, "1011","ddddd","0","0","1","0","00")
MPY_ENC(F2_sffixupn, "1011","ddddd","0","0","1","1","00")
MPY_ENC(F2_sffixupd, "1011","ddddd","0","0","1","1","01")
+MPY_ENC(F2_sfrecipa, "1011","ddddd","1","1","1","1","ee")
DEF_FIELDROW_DESC32(ICLASS_M" 1100 -------- PP------ --------","[#12] Rd=(Rs,Rt)")
DEF_FIELD32(ICLASS_M" 1100 -------- PP------ --!-----",Mc_tH,"Rt is High") /*Rt high */
@@ -1641,6 +1667,7 @@ SH2_RR_ENC(F2_conv_sf2w, "1011","100","-","000","ddddd")
SH2_RR_ENC(F2_conv_sf2uw_chop, "1011","011","-","001","ddddd")
SH2_RR_ENC(F2_conv_sf2w_chop, "1011","100","-","001","ddddd")
SH2_RR_ENC(F2_sffixupr, "1011","101","-","000","ddddd")
+SH2_RR_ENC(F2_sfinvsqrta, "1011","111","-","0ee","ddddd")
DEF_FIELDROW_DESC32(ICLASS_S2op" 1100 -------- PP------ --------","[#12] Rd=(Rs,#u6)")
@@ -1740,11 +1767,14 @@ SH_RRR_ENC(S4_vxsubaddh, "0001","01-","-","110","ddddd")
SH_RRR_ENC(S4_vxaddsubhr, "0001","11-","-","00-","ddddd")
SH_RRR_ENC(S4_vxsubaddhr, "0001","11-","-","01-","ddddd")
SH_RRR_ENC(S4_extractp_rp, "0001","11-","-","10-","ddddd")
+SH_RRR_ENC(S2_cabacdecbin, "0001","11-","-","11-","ddddd") /* implicit P0 write */
DEF_FIELDROW_DESC32(ICLASS_S3op" 0010 -------- PP------ --------","[#2] Rdd=(Rss,Rtt,Pu)")
SH_RRR_ENC(S2_valignrb, "0010","0--","-","-uu","ddddd")
SH_RRR_ENC(S2_vsplicerb, "0010","100","-","-uu","ddddd")
+SH_RRR_ENC(A4_addp_c, "0010","110","-","-xx","ddddd")
+SH_RRR_ENC(A4_subp_c, "0010","111","-","-xx","ddddd")
DEF_FIELDROW_DESC32(ICLASS_S3op" 0011 -------- PP------ --------","[#3] Rdd=(Rss,Rt)")
diff --git a/target/hexagon/imported/float.idef b/target/hexagon/imported/float.idef
index 76cecfebf5..3e75bc4604 100644
--- a/target/hexagon/imported/float.idef
+++ b/target/hexagon/imported/float.idef
@@ -146,6 +146,22 @@ Q6INSN(F2_sfimm_n,"Rd32=sfmake(#u10):neg",ATTRIBS(),
})
+Q6INSN(F2_sfrecipa,"Rd32,Pe4=sfrecipa(Rs32,Rt32)",ATTRIBS(),
+"Reciprocal Approximation for Division",
+{
+ fHIDE(int idx;)
+ fHIDE(int adjust;)
+ fHIDE(int mant;)
+ fHIDE(int exp;)
+ if (fSF_RECIP_COMMON(RsV,RtV,RdV,adjust)) {
+ PeV = adjust;
+ idx = (RtV >> 16) & 0x7f;
+ mant = (fSF_RECIP_LOOKUP(idx) << 15) | 1;
+ exp = fSF_BIAS() - (fSF_GETEXP(RtV) - fSF_BIAS()) - 1;
+ RdV = fMAKESF(fGETBIT(31,RtV),exp,mant);
+ }
+})
+
Q6INSN(F2_sffixupn,"Rd32=sffixupn(Rs32,Rt32)",ATTRIBS(),
"Fix Up Numerator",
{
@@ -162,6 +178,22 @@ Q6INSN(F2_sffixupd,"Rd32=sffixupd(Rs32,Rt32)",ATTRIBS(),
RdV = RtV;
})
+Q6INSN(F2_sfinvsqrta,"Rd32,Pe4=sfinvsqrta(Rs32)",ATTRIBS(),
+"Reciprocal Square Root Approximation",
+{
+ fHIDE(int idx;)
+ fHIDE(int adjust;)
+ fHIDE(int mant;)
+ fHIDE(int exp;)
+ if (fSF_INVSQRT_COMMON(RsV,RdV,adjust)) {
+ PeV = adjust;
+ idx = (RsV >> 17) & 0x7f;
+ mant = (fSF_INVSQRT_LOOKUP(idx) << 15);
+ exp = fSF_BIAS() - ((fSF_GETEXP(RsV) - fSF_BIAS()) >> 1) - 1;
+ RdV = fMAKESF(fGETBIT(31,RsV),exp,mant);
+ }
+})
+
Q6INSN(F2_sffixupr,"Rd32=sffixupr(Rs32)",ATTRIBS(),
"Fix Up Radicand",
{
diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef
index 78a2ea441c..359d3b744e 100644
--- a/target/hexagon/imported/ldst.idef
+++ b/target/hexagon/imported/ldst.idef
@@ -25,7 +25,10 @@ Q6INSN(L2_##TAG##_io, OPER"(Rs32+#s11:"SHFT")", ATTRIB,DESCR,{fIMMEXT(
Q6INSN(L4_##TAG##_ur, OPER"(Rt32<<#u2+#U6)", ATTRIB,DESCR,{fMUST_IMMEXT(UiV); fEA_IRs(UiV,RtV,uiV); SEMANTICS;})\
Q6INSN(L4_##TAG##_ap, OPER"(Re32=#U6)", ATTRIB,DESCR,{fMUST_IMMEXT(UiV); fEA_IMM(UiV); SEMANTICS; ReV=UiV; })\
Q6INSN(L2_##TAG##_pr, OPER"(Rx32++Mu2)", ATTRIB,DESCR,{fEA_REG(RxV); fPM_M(RxV,MuV); SEMANTICS;})\
+Q6INSN(L2_##TAG##_pbr, OPER"(Rx32++Mu2:brev)", ATTRIB,DESCR,{fEA_BREVR(RxV); fPM_M(RxV,MuV); SEMANTICS;})\
Q6INSN(L2_##TAG##_pi, OPER"(Rx32++#s4:"SHFT")", ATTRIB,DESCR,{fEA_REG(RxV); fPM_I(RxV,siV); SEMANTICS;})\
+Q6INSN(L2_##TAG##_pci, OPER"(Rx32++#s4:"SHFT":circ(Mu2))",ATTRIB,DESCR,{fEA_REG(RxV); fPM_CIRI(RxV,siV,MuV); SEMANTICS;})\
+Q6INSN(L2_##TAG##_pcr, OPER"(Rx32++I:circ(Mu2))", ATTRIB,DESCR,{fEA_REG(RxV); fPM_CIRR(RxV,fREAD_IREG(MuV)<<SCALE,MuV); SEMANTICS;})
/* The set of 32-bit load instructions */
STD_LD_AMODES(loadrub,"Rd32=memub","Load Unsigned Byte",ATTRIBS(A_LOAD),"0",fLOAD(1,1,u,EA,RdV),0)
@@ -35,6 +38,68 @@ STD_LD_AMODES(loadrh, "Rd32=memh", "Load signed Half integer",ATTRIBS(A_LOAD),"1
STD_LD_AMODES(loadri, "Rd32=memw", "Load Word",ATTRIBS(A_LOAD),"2",fLOAD(1,4,u,EA,RdV),2)
STD_LD_AMODES(loadrd, "Rdd32=memd","Load Double integer",ATTRIBS(A_LOAD),"3",fLOAD(1,8,u,EA,RddV),3)
+/* These instructions do a load an unpack */
+STD_LD_AMODES(loadbzw2, "Rd32=memubh", "Load Bytes and Vector Zero-Extend (unpack)",
+ATTRIBS(A_LOAD),"1",
+{fHIDE(size2u_t tmpV; int i;)
+ fLOAD(1,2,u,EA,tmpV);
+ for (i=0;i<2;i++) {
+ fSETHALF(i,RdV,fGETUBYTE(i,tmpV));
+ }
+},1)
+
+STD_LD_AMODES(loadbzw4, "Rdd32=memubh", "Load Bytes and Vector Zero-Extend (unpack)",
+ATTRIBS(A_LOAD),"2",
+{fHIDE(size4u_t tmpV; int i;)
+ fLOAD(1,4,u,EA,tmpV);
+ for (i=0;i<4;i++) {
+ fSETHALF(i,RddV,fGETUBYTE(i,tmpV));
+ }
+},2)
+
+
+
+/* These instructions do a load an unpack */
+STD_LD_AMODES(loadbsw2, "Rd32=membh", "Load Bytes and Vector Sign-Extend (unpack)",
+ATTRIBS(A_LOAD),"1",
+{fHIDE(size2u_t tmpV; int i;)
+ fLOAD(1,2,u,EA,tmpV);
+ for (i=0;i<2;i++) {
+ fSETHALF(i,RdV,fGETBYTE(i,tmpV));
+ }
+},1)
+
+STD_LD_AMODES(loadbsw4, "Rdd32=membh", "Load Bytes and Vector Sign-Extend (unpack)",
+ATTRIBS(A_LOAD),"2",
+{fHIDE(size4u_t tmpV; int i;)
+ fLOAD(1,4,u,EA,tmpV);
+ for (i=0;i<4;i++) {
+ fSETHALF(i,RddV,fGETBYTE(i,tmpV));
+ }
+},2)
+
+
+
+STD_LD_AMODES(loadalignh, "Ryy32=memh_fifo", "Load Half-word into shifted vector",
+ATTRIBS(A_LOAD),"1",
+{
+ fHIDE(size8u_t tmpV;)
+ fLOAD(1,2,u,EA,tmpV);
+ RyyV = (((size8u_t)RyyV)>>16)|(tmpV<<48);
+},1)
+
+
+STD_LD_AMODES(loadalignb, "Ryy32=memb_fifo", "Load byte into shifted vector",
+ATTRIBS(A_LOAD),"0",
+{
+ fHIDE(size8u_t tmpV;)
+ fLOAD(1,1,u,EA,tmpV);
+ RyyV = (((size8u_t)RyyV)>>8)|(tmpV<<56);
+},0)
+
+
+
+
/* The set of addressing modes standard to all Store instructions */
#define STD_ST_AMODES(TAG,DEST,OPER,DESCR,ATTRIB,SHFT,SEMANTICS,SCALE)\
Q6INSN(S2_##TAG##_io, OPER"(Rs32+#s11:"SHFT")="DEST, ATTRIB,DESCR,{fIMMEXT(siV); fEA_RI(RsV,siV); SEMANTICS; })\
@@ -42,6 +107,9 @@ Q6INSN(S2_##TAG##_pi, OPER"(Rx32++#s4:"SHFT")="DEST, ATTRIB,DESCR,{fEA_REG(
Q6INSN(S4_##TAG##_ap, OPER"(Re32=#U6)="DEST, ATTRIB,DESCR,{fMUST_IMMEXT(UiV); fEA_IMM(UiV); SEMANTICS; ReV=UiV; })\
Q6INSN(S2_##TAG##_pr, OPER"(Rx32++Mu2)="DEST, ATTRIB,DESCR,{fEA_REG(RxV); fPM_M(RxV,MuV); SEMANTICS; })\
Q6INSN(S4_##TAG##_ur, OPER"(Ru32<<#u2+#U6)="DEST, ATTRIB,DESCR,{fMUST_IMMEXT(UiV); fEA_IRs(UiV,RuV,uiV); SEMANTICS;})\
+Q6INSN(S2_##TAG##_pbr, OPER"(Rx32++Mu2:brev)="DEST, ATTRIB,DESCR,{fEA_BREVR(RxV); fPM_M(RxV,MuV); SEMANTICS; })\
+Q6INSN(S2_##TAG##_pci, OPER"(Rx32++#s4:"SHFT":circ(Mu2))="DEST, ATTRIB,DESCR,{fEA_REG(RxV); fPM_CIRI(RxV,siV,MuV); SEMANTICS;})\
+Q6INSN(S2_##TAG##_pcr, OPER"(Rx32++I:circ(Mu2))="DEST, ATTRIB,DESCR,{fEA_REG(RxV); fPM_CIRR(RxV,fREAD_IREG(MuV)<<SCALE,MuV); SEMANTICS;})
/* The set of 32-bit store instructions */
diff --git a/target/hexagon/imported/macros.def b/target/hexagon/imported/macros.def
index 65292c7afa..32ed3bf8fc 100755
--- a/target/hexagon/imported/macros.def
+++ b/target/hexagon/imported/macros.def
@@ -92,6 +92,21 @@ DEF_MACRO(
/* attribs */
)
+
+DEF_MACRO(
+ fINSERT_RANGE,
+ {
+ int offset=LOWBIT;
+ int width=HIBIT-LOWBIT+1;
+ /* clear bits where new bits go */
+ INREG &= ~(((fCONSTLL(1)<<width)-1)<<offset);
+ /* OR in new bits */
+ INREG |= ((INVAL & ((fCONSTLL(1)<<width)-1)) << offset);
+ },
+ /* attribs */
+)
+
+
DEF_MACRO(
f8BITSOF,
( (VAL) ? 0xff : 0x00),
@@ -277,6 +292,12 @@ DEF_MACRO(
/*************************************/
DEF_MACRO(
+ fREAD_IREG, /* read modifier register */
+ (fSXTN(11,64,(((VAL) & 0xf0000000)>>21) | ((VAL>>17)&0x7f) )), /* behavior */
+ ()
+)
+
+DEF_MACRO(
fREAD_LR, /* read link register */
(READ_RREG(REG_LR)), /* behavior */
()
@@ -307,6 +328,12 @@ DEF_MACRO(
)
DEF_MACRO(
+ fREAD_CSREG, /* read CS register */
+ (READ_RREG(REG_CSA+N)), /* behavior */
+ ()
+)
+
+DEF_MACRO(
fREAD_LC0, /* read loop count */
(READ_RREG(REG_LC0)), /* behavior */
()
@@ -807,6 +834,12 @@ DEF_MACRO(
)
DEF_MACRO(
+ fEA_BREVR, /* Calculate EA with bit reversed bottom of REGISTER */
+ EA=fbrev(REG),
+ ()
+)
+
+DEF_MACRO(
fEA_GPI, /* Calculate EA with Global Poitner + Immediate */
do { EA=fREAD_GP()+IMM; fGP_DOCHKPAGECROSS(fREAD_GP(),EA); } while (0),
()
@@ -825,6 +858,20 @@ DEF_MACRO(
)
DEF_MACRO(
+ fPM_CIRI, /* Post Modify Register using Circular arithmetic by Immediate */
+ do { fcirc_add(REG,siV,MuV); } while (0),
+ ()
+)
+
+DEF_MACRO(
+ fPM_CIRR, /* Post Modify Register using Circular arithmetic by register */
+ do { fcirc_add(REG,VAL,MuV); } while (0),
+ ()
+)
+
+
+
+DEF_MACRO(
fSCALE, /* scale by N */
(((size8s_t)(A))<<N),
/* optional attributes */
diff --git a/target/hexagon/imported/shift.idef b/target/hexagon/imported/shift.idef
index e328ab7329..b32c4e04d1 100644
--- a/target/hexagon/imported/shift.idef
+++ b/target/hexagon/imported/shift.idef
@@ -1029,6 +1029,53 @@ Q6INSN(S4_clbpaddi,"Rd32=add(clb(Rss32),#s6)",ATTRIBS(A_ARCHV2),
{ RdV = (fMAX(fCL1_8(RssV),fCL1_8(~RssV)))+siV;})
+
+Q6INSN(S2_cabacdecbin,"Rdd32=decbin(Rss32,Rtt32)",ATTRIBS(A_ARCHV3),"CABAC decode bin",
+{
+ fHIDE(size4u_t state;)
+ fHIDE(size4u_t valMPS;)
+ fHIDE(size4u_t bitpos;)
+ fHIDE(size4u_t range;)
+ fHIDE(size4u_t offset;)
+ fHIDE(size4u_t rLPS;)
+ fHIDE(size4u_t rMPS;)
+
+ state = fEXTRACTU_RANGE( fGETWORD(1,RttV) ,5,0);
+ valMPS = fEXTRACTU_RANGE( fGETWORD(1,RttV) ,8,8);
+ bitpos = fEXTRACTU_RANGE( fGETWORD(0,RttV) ,4,0);
+ range = fGETWORD(0,RssV);
+ offset = fGETWORD(1,RssV);
+
+ /* calculate rLPS */
+ range <<= bitpos;
+ offset <<= bitpos;
+ rLPS = rLPS_table_64x4[state][ (range >>29)&3];
+ rLPS = rLPS << 23; /* left aligned */
+
+ /* calculate rMPS */
+ rMPS= (range&0xff800000) - rLPS;
+
+ /* most probable region */
+ if (offset < rMPS) {
+ RddV = AC_next_state_MPS_64[state];
+ fINSERT_RANGE(RddV,8,8,valMPS);
+ fINSERT_RANGE(RddV,31,23,(rMPS>>23));
+ fSETWORD(1,RddV,offset);
+ fWRITE_P0(valMPS);
+
+
+ }
+ /* least probable region */
+ else {
+ RddV = AC_next_state_LPS_64[state];
+ fINSERT_RANGE(RddV,8,8,((!state)?(1-valMPS):(valMPS)));
+ fINSERT_RANGE(RddV,31,23,(rLPS>>23));
+ fSETWORD(1,RddV,(offset-rMPS));
+ fWRITE_P0((valMPS^1));
+ }
+})
+
+
Q6INSN(S2_clb,"Rd32=clb(Rs32)",ATTRIBS(),
"Count leading bits", {RdV = fMAX(fCL1_4(RsV),fCL1_4(~RsV));})
diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h
index 5756a1d0ca..2e345912a8 100644
--- a/target/hexagon/insn.h
+++ b/target/hexagon/insn.h
@@ -40,14 +40,15 @@ struct Instruction {
uint32_t iclass:6;
uint32_t slot:3;
- uint32_t part1:1; /*
+ uint32_t which_extended:1; /* If has an extender, which immediate */
+ uint32_t new_value_producer_slot:4;
+
+ bool part1; /*
* cmp-jumps are split into two insns.
* set for the compare and clear for the jump
*/
- uint32_t extension_valid:1; /* Has a constant extender attached */
- uint32_t which_extended:1; /* If has an extender, which immediate */
- uint32_t is_endloop:1; /* This is an end of loop */
- uint32_t new_value_producer_slot:4;
+ bool extension_valid; /* Has a constant extender attached */
+ bool is_endloop; /* This is an end of loop */
int32_t immed[IMMEDS_MAX]; /* immediate field */
};
@@ -58,13 +59,13 @@ struct Packet {
uint16_t encod_pkt_size_in_bytes;
/* Pre-decodes about COF */
- uint32_t pkt_has_cof:1; /* Has any change-of-flow */
- uint32_t pkt_has_endloop:1;
+ bool pkt_has_cof; /* Has any change-of-flow */
+ bool pkt_has_endloop;
- uint32_t pkt_has_dczeroa:1;
+ bool pkt_has_dczeroa;
- uint32_t pkt_has_store_s0:1;
- uint32_t pkt_has_store_s1:1;
+ bool pkt_has_store_s0;
+ bool pkt_has_store_s1;
Insn insn[INSTRUCTIONS_MAX];
};
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h
index 2da85c8606..6b20affdfa 100644
--- a/target/hexagon/internal.h
+++ b/target/hexagon/internal.h
@@ -22,11 +22,12 @@
* Change HEX_DEBUG to 1 to turn on debugging output
*/
#define HEX_DEBUG 0
-#if HEX_DEBUG
-#define HEX_DEBUG_LOG(...) qemu_log(__VA_ARGS__)
-#else
-#define HEX_DEBUG_LOG(...) do { } while (0)
-#endif
+#define HEX_DEBUG_LOG(...) \
+ do { \
+ if (HEX_DEBUG) { \
+ qemu_log(__VA_ARGS__); \
+ } \
+ } while (0)
int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
index cfcb8173ba..b726c3b791 100644
--- a/target/hexagon/macros.h
+++ b/target/hexagon/macros.h
@@ -133,6 +133,38 @@
CHECK_NOSHUF; \
tcg_gen_qemu_ld64(DST, VA, ctx->mem_idx); \
} while (0)
+
+#define MEM_STORE1_FUNC(X) \
+ __builtin_choose_expr(TYPE_INT(X), \
+ gen_store1i, \
+ __builtin_choose_expr(TYPE_TCGV(X), \
+ gen_store1, (void)0))
+#define MEM_STORE1(VA, DATA, SLOT) \
+ MEM_STORE1_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT)
+
+#define MEM_STORE2_FUNC(X) \
+ __builtin_choose_expr(TYPE_INT(X), \
+ gen_store2i, \
+ __builtin_choose_expr(TYPE_TCGV(X), \
+ gen_store2, (void)0))
+#define MEM_STORE2(VA, DATA, SLOT) \
+ MEM_STORE2_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT)
+
+#define MEM_STORE4_FUNC(X) \
+ __builtin_choose_expr(TYPE_INT(X), \
+ gen_store4i, \
+ __builtin_choose_expr(TYPE_TCGV(X), \
+ gen_store4, (void)0))
+#define MEM_STORE4(VA, DATA, SLOT) \
+ MEM_STORE4_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT)
+
+#define MEM_STORE8_FUNC(X) \
+ __builtin_choose_expr(TYPE_INT(X), \
+ gen_store8i, \
+ __builtin_choose_expr(TYPE_TCGV_I64(X), \
+ gen_store8, (void)0))
+#define MEM_STORE8(VA, DATA, SLOT) \
+ MEM_STORE8_FUNC(DATA)(cpu_env, VA, DATA, ctx, SLOT)
#else
#define MEM_LOAD1s(VA) ((int8_t)mem_load1(env, slot, VA))
#define MEM_LOAD1u(VA) ((uint8_t)mem_load1(env, slot, VA))
@@ -190,6 +222,13 @@ static inline void gen_pred_cancel(TCGv pred, int slot_num)
(((HIBIT) - (LOWBIT) + 1) ? \
extract64((INREG), (LOWBIT), ((HIBIT) - (LOWBIT) + 1)) : \
0LL)
+#define fINSERT_RANGE(INREG, HIBIT, LOWBIT, INVAL) \
+ do { \
+ int width = ((HIBIT) - (LOWBIT) + 1); \
+ INREG = (width >= 0 ? \
+ deposit64((INREG), (LOWBIT), width, (INVAL)) : \
+ INREG); \
+ } while (0)
#define f8BITSOF(VAL) ((VAL) ? 0xff : 0x00)
@@ -285,6 +324,39 @@ static inline void gen_logical_not(TCGv dest, TCGv src)
#define fPCALIGN(IMM) IMM = (IMM & ~PCALIGN_MASK)
+#ifdef QEMU_GENERATE
+static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift)
+{
+ /*
+ * Section 2.2.4 of the Hexagon V67 Programmer's Reference Manual
+ *
+ * The "I" value from a modifier register is divided into two pieces
+ * LSB bits 23:17
+ * MSB bits 31:28
+ * The value is signed
+ *
+ * At the end we shift the result according to the shift argument
+ */
+ TCGv msb = tcg_temp_new();
+ TCGv lsb = tcg_temp_new();
+
+ tcg_gen_extract_tl(lsb, val, 17, 7);
+ tcg_gen_sari_tl(msb, val, 21);
+ tcg_gen_deposit_tl(result, msb, lsb, 0, 7);
+
+ tcg_gen_shli_tl(result, result, shift);
+
+ tcg_temp_free(msb);
+ tcg_temp_free(lsb);
+
+ return result;
+}
+#define fREAD_IREG(VAL, SHIFT) gen_read_ireg(ireg, (VAL), (SHIFT))
+#else
+#define fREAD_IREG(VAL) \
+ (fSXTN(11, 64, (((VAL) & 0xf0000000) >> 21) | ((VAL >> 17) & 0x7f)))
+#endif
+
#define fREAD_LR() (READ_REG(HEX_REG_LR))
#define fWRITE_LR(A) WRITE_RREG(HEX_REG_LR, A)
@@ -341,8 +413,6 @@ static inline void gen_logical_not(TCGv dest, TCGv src)
#define fWRITE_LC0(VAL) WRITE_RREG(HEX_REG_LC0, VAL)
#define fWRITE_LC1(VAL) WRITE_RREG(HEX_REG_LC1, VAL)
-#define fCARRY_FROM_ADD(A, B, C) carry_from_add64(A, B, C)
-
#define fSET_OVERFLOW() SET_USR_FIELD(USR_OVF, 1)
#define fSET_LPCFG(VAL) SET_USR_FIELD(USR_LPCFG, (VAL))
#define fGET_LPCFG (GET_USR_FIELD(USR_LPCFG))
@@ -402,6 +472,21 @@ static inline void gen_logical_not(TCGv dest, TCGv src)
#define fCAST8S_16S(A) (int128_exts64(A))
#define fCAST16S_8S(A) (int128_getlo(A))
+#ifdef QEMU_GENERATE
+#define fEA_RI(REG, IMM) tcg_gen_addi_tl(EA, REG, IMM)
+#define fEA_RRs(REG, REG2, SCALE) \
+ do { \
+ TCGv tmp = tcg_temp_new(); \
+ tcg_gen_shli_tl(tmp, REG2, SCALE); \
+ tcg_gen_add_tl(EA, REG, tmp); \
+ tcg_temp_free(tmp); \
+ } while (0)
+#define fEA_IRs(IMM, REG, SCALE) \
+ do { \
+ tcg_gen_shli_tl(EA, REG, SCALE); \
+ tcg_gen_addi_tl(EA, EA, IMM); \
+ } while (0)
+#else
#define fEA_RI(REG, IMM) \
do { \
EA = REG + IMM; \
@@ -414,12 +499,21 @@ static inline void gen_logical_not(TCGv dest, TCGv src)
do { \
EA = IMM + (REG << SCALE); \
} while (0)
+#endif
#ifdef QEMU_GENERATE
#define fEA_IMM(IMM) tcg_gen_movi_tl(EA, IMM)
#define fEA_REG(REG) tcg_gen_mov_tl(EA, REG)
+#define fEA_BREVR(REG) gen_helper_fbrev(EA, REG)
#define fPM_I(REG, IMM) tcg_gen_addi_tl(REG, REG, IMM)
#define fPM_M(REG, MVAL) tcg_gen_add_tl(REG, REG, MVAL)
+#define fPM_CIRI(REG, IMM, MVAL) \
+ do { \
+ TCGv tcgv_siV = tcg_const_tl(siV); \
+ gen_helper_fcircadd(REG, REG, tcgv_siV, MuV, \
+ hex_gpr[HEX_REG_CS0 + MuN]); \
+ tcg_temp_free(tcgv_siV); \
+ } while (0)
#else
#define fEA_IMM(IMM) do { EA = (IMM); } while (0)
#define fEA_REG(REG) do { EA = (REG); } while (0)
@@ -496,23 +590,43 @@ static inline void gen_logical_not(TCGv dest, TCGv src)
gen_load_locked##SIZE##SIGN(DST, EA, ctx->mem_idx);
#endif
+#ifdef QEMU_GENERATE
+#define fSTORE(NUM, SIZE, EA, SRC) MEM_STORE##SIZE(EA, SRC, insn->slot)
+#else
#define fSTORE(NUM, SIZE, EA, SRC) MEM_STORE##SIZE(EA, SRC, slot)
+#endif
#ifdef QEMU_GENERATE
#define fSTORE_LOCKED(NUM, SIZE, EA, SRC, PRED) \
gen_store_conditional##SIZE(env, ctx, PdN, PRED, EA, SRC);
#endif
+#ifdef QEMU_GENERATE
+#define GETBYTE_FUNC(X) \
+ __builtin_choose_expr(TYPE_TCGV(X), \
+ gen_get_byte, \
+ __builtin_choose_expr(TYPE_TCGV_I64(X), \
+ gen_get_byte_i64, (void)0))
+#define fGETBYTE(N, SRC) GETBYTE_FUNC(SRC)(BYTE, N, SRC, true)
+#define fGETUBYTE(N, SRC) GETBYTE_FUNC(SRC)(BYTE, N, SRC, false)
+#else
#define fGETBYTE(N, SRC) ((int8_t)((SRC >> ((N) * 8)) & 0xff))
#define fGETUBYTE(N, SRC) ((uint8_t)((SRC >> ((N) * 8)) & 0xff))
+#endif
#define fSETBYTE(N, DST, VAL) \
do { \
DST = (DST & ~(0x0ffLL << ((N) * 8))) | \
(((uint64_t)((VAL) & 0x0ffLL)) << ((N) * 8)); \
} while (0)
+
+#ifdef QEMU_GENERATE
+#define fGETHALF(N, SRC) gen_get_half(HALF, N, SRC, true)
+#define fGETUHALF(N, SRC) gen_get_half(HALF, N, SRC, false)
+#else
#define fGETHALF(N, SRC) ((int16_t)((SRC >> ((N) * 16)) & 0xffff))
#define fGETUHALF(N, SRC) ((uint16_t)((SRC >> ((N) * 16)) & 0xffff))
+#endif
#define fSETHALF(N, DST, VAL) \
do { \
DST = (DST & ~(0x0ffffLL << ((N) * 16))) | \
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build
index bb0b4fb621..6fd9360b74 100644
--- a/target/hexagon/meson.build
+++ b/target/hexagon/meson.build
@@ -173,7 +173,6 @@ hexagon_ss.add(files(
'printinsn.c',
'arch.c',
'fma_emu.c',
- 'conv_emu.c',
))
target_arch += {'hexagon': hexagon_ss}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 2c6d718579..63dd685658 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -25,7 +25,6 @@
#include "arch.h"
#include "hex_arch_types.h"
#include "fma_emu.h"
-#include "conv_emu.h"
#define SF_BIAS 127
#define SF_MANTBITS 23
@@ -35,7 +34,7 @@ static void QEMU_NORETURN do_raise_exception_err(CPUHexagonState *env,
uint32_t exception,
uintptr_t pc)
{
- CPUState *cs = CPU(hexagon_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception);
cs->exception_index = exception;
cpu_loop_exit_restore(cs, pc);
@@ -46,8 +45,8 @@ void QEMU_NORETURN HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp)
do_raise_exception_err(env, excp, 0);
}
-static inline void log_reg_write(CPUHexagonState *env, int rnum,
- target_ulong val, uint32_t slot)
+static void log_reg_write(CPUHexagonState *env, int rnum,
+ target_ulong val, uint32_t slot)
{
HEX_DEBUG_LOG("log_reg_write[%d] = " TARGET_FMT_ld " (0x" TARGET_FMT_lx ")",
rnum, val, val);
@@ -57,14 +56,13 @@ static inline void log_reg_write(CPUHexagonState *env, int rnum,
HEX_DEBUG_LOG("\n");
env->new_value[rnum] = val;
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- env->reg_written[rnum] = 1;
-#endif
+ if (HEX_DEBUG) {
+ /* Do this so HELPER(debug_commit_end) will know */
+ env->reg_written[rnum] = 1;
+ }
}
-static inline void log_pred_write(CPUHexagonState *env, int pnum,
- target_ulong val)
+static void log_pred_write(CPUHexagonState *env, int pnum, target_ulong val)
{
HEX_DEBUG_LOG("log_pred_write[%d] = " TARGET_FMT_ld
" (0x" TARGET_FMT_lx ")\n",
@@ -79,8 +77,8 @@ static inline void log_pred_write(CPUHexagonState *env, int pnum,
}
}
-static inline void log_store32(CPUHexagonState *env, target_ulong addr,
- target_ulong val, int width, int slot)
+static void log_store32(CPUHexagonState *env, target_ulong addr,
+ target_ulong val, int width, int slot)
{
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
", %" PRId32 " [0x08%" PRIx32 "])\n",
@@ -90,8 +88,8 @@ static inline void log_store32(CPUHexagonState *env, target_ulong addr,
env->mem_log_stores[slot].data32 = val;
}
-static inline void log_store64(CPUHexagonState *env, target_ulong addr,
- int64_t val, int width, int slot)
+static void log_store64(CPUHexagonState *env, target_ulong addr,
+ int64_t val, int width, int slot)
{
HEX_DEBUG_LOG("log_store%d(0x" TARGET_FMT_lx
", %" PRId64 " [0x016%" PRIx64 "])\n",
@@ -101,7 +99,7 @@ static inline void log_store64(CPUHexagonState *env, target_ulong addr,
env->mem_log_stores[slot].data64 = val;
}
-static inline void write_new_pc(CPUHexagonState *env, target_ulong addr)
+static void write_new_pc(CPUHexagonState *env, target_ulong addr)
{
HEX_DEBUG_LOG("write_new_pc(0x" TARGET_FMT_lx ")\n", addr);
@@ -119,7 +117,6 @@ static inline void write_new_pc(CPUHexagonState *env, target_ulong addr)
}
}
-#if HEX_DEBUG
/* Handy place to set a breakpoint */
void HELPER(debug_start_packet)(CPUHexagonState *env)
{
@@ -130,14 +127,12 @@ void HELPER(debug_start_packet)(CPUHexagonState *env)
env->reg_written[i] = 0;
}
}
-#endif
-static inline int32_t new_pred_value(CPUHexagonState *env, int pnum)
+static int32_t new_pred_value(CPUHexagonState *env, int pnum)
{
return env->new_pred_value[pnum];
}
-#if HEX_DEBUG
/* Checks for bookkeeping errors between disassembly context and runtime */
void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check)
{
@@ -147,7 +142,6 @@ void HELPER(debug_check_store_width)(CPUHexagonState *env, int slot, int check)
g_assert_not_reached();
}
}
-#endif
void HELPER(commit_store)(CPUHexagonState *env, int slot_num)
{
@@ -173,7 +167,6 @@ void HELPER(commit_store)(CPUHexagonState *env, int slot_num)
}
}
-#if HEX_DEBUG
static void print_store(CPUHexagonState *env, int slot)
{
if (!(env->slot_cancelled & (1 << slot))) {
@@ -257,35 +250,26 @@ void HELPER(debug_commit_end)(CPUHexagonState *env, int has_st0, int has_st1)
env->gpr[HEX_REG_QEMU_INSN_CNT]);
}
-#endif
-
-static int32_t fcircadd_v4(int32_t RxV, int32_t offset, int32_t M, int32_t CS)
-{
- int32_t length = M & 0x0001ffff;
- uint32_t new_ptr = RxV + offset;
- uint32_t start_addr = CS;
- uint32_t end_addr = start_addr + length;
-
- if (new_ptr >= end_addr) {
- new_ptr -= length;
- } else if (new_ptr < start_addr) {
- new_ptr += length;
- }
-
- return new_ptr;
-}
int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS)
{
- int32_t K_const = (M >> 24) & 0xf;
- int32_t length = M & 0x1ffff;
- int32_t mask = (1 << (K_const + 2)) - 1;
+ int32_t K_const = sextract32(M, 24, 4);
+ int32_t length = sextract32(M, 0, 17);
uint32_t new_ptr = RxV + offset;
- uint32_t start_addr = RxV & (~mask);
- uint32_t end_addr = start_addr | length;
+ uint32_t start_addr;
+ uint32_t end_addr;
if (K_const == 0 && length >= 4) {
- return fcircadd_v4(RxV, offset, M, CS);
+ start_addr = CS;
+ end_addr = start_addr + length;
+ } else {
+ /*
+ * Versions v3 and earlier used the K value to specify a power-of-2 size
+ * 2^(K+2) that is greater than the buffer length
+ */
+ int32_t mask = (1 << (K_const + 2)) - 1;
+ start_addr = RxV & (~mask);
+ end_addr = start_addr | length;
}
if (new_ptr >= end_addr) {
@@ -297,24 +281,103 @@ int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS)
return new_ptr;
}
+uint32_t HELPER(fbrev)(uint32_t addr)
+{
+ /*
+ * Bit reverse the low 16 bits of the address
+ */
+ return deposit32(addr, 0, 16, revbit16(addr));
+}
+
+static float32 build_float32(uint8_t sign, uint32_t exp, uint32_t mant)
+{
+ return make_float32(
+ ((sign & 1) << 31) |
+ ((exp & 0xff) << SF_MANTBITS) |
+ (mant & ((1 << SF_MANTBITS) - 1)));
+}
+
/*
- * Hexagon FP operations return ~0 insteat of NaN
- * The hex_check_sfnan/hex_check_dfnan functions perform this check
+ * sfrecipa, sfinvsqrta have two 32-bit results
+ * r0,p0=sfrecipa(r1,r2)
+ * r0,p0=sfinvsqrta(r1)
+ *
+ * Since helpers can only return a single value, we pack the two results
+ * into a 64-bit value.
*/
-static float32 hex_check_sfnan(float32 x)
+uint64_t HELPER(sfrecipa)(CPUHexagonState *env, float32 RsV, float32 RtV)
{
- if (float32_is_any_nan(x)) {
- return make_float32(0xFFFFFFFFU);
+ int32_t PeV = 0;
+ float32 RdV;
+ int idx;
+ int adjust;
+ int mant;
+ int exp;
+
+ arch_fpop_start(env);
+ if (arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status)) {
+ PeV = adjust;
+ idx = (RtV >> 16) & 0x7f;
+ mant = (recip_lookup_table[idx] << 15) | 1;
+ exp = SF_BIAS - (float32_getexp(RtV) - SF_BIAS) - 1;
+ RdV = build_float32(extract32(RtV, 31, 1), exp, mant);
+ }
+ arch_fpop_end(env);
+ return ((uint64_t)RdV << 32) | PeV;
+}
+
+uint64_t HELPER(sfinvsqrta)(CPUHexagonState *env, float32 RsV)
+{
+ int PeV = 0;
+ float32 RdV;
+ int idx;
+ int adjust;
+ int mant;
+ int exp;
+
+ arch_fpop_start(env);
+ if (arch_sf_invsqrt_common(&RsV, &RdV, &adjust, &env->fp_status)) {
+ PeV = adjust;
+ idx = (RsV >> 17) & 0x7f;
+ mant = (invsqrt_lookup_table[idx] << 15);
+ exp = SF_BIAS - ((float32_getexp(RsV) - SF_BIAS) >> 1) - 1;
+ RdV = build_float32(extract32(RsV, 31, 1), exp, mant);
+ }
+ arch_fpop_end(env);
+ return ((uint64_t)RdV << 32) | PeV;
+}
+
+int64_t HELPER(vacsh_val)(CPUHexagonState *env,
+ int64_t RxxV, int64_t RssV, int64_t RttV)
+{
+ for (int i = 0; i < 4; i++) {
+ int xv = sextract64(RxxV, i * 16, 16);
+ int sv = sextract64(RssV, i * 16, 16);
+ int tv = sextract64(RttV, i * 16, 16);
+ int max;
+ xv = xv + tv;
+ sv = sv - tv;
+ max = xv > sv ? xv : sv;
+ /* Note that fSATH can set the OVF bit in usr */
+ RxxV = deposit64(RxxV, i * 16, 16, fSATH(max));
}
- return x;
+ return RxxV;
}
-static float64 hex_check_dfnan(float64 x)
+int32_t HELPER(vacsh_pred)(CPUHexagonState *env,
+ int64_t RxxV, int64_t RssV, int64_t RttV)
{
- if (float64_is_any_nan(x)) {
- return make_float64(0xFFFFFFFFFFFFFFFFULL);
+ int32_t PeV = 0;
+ for (int i = 0; i < 4; i++) {
+ int xv = sextract64(RxxV, i * 16, 16);
+ int sv = sextract64(RssV, i * 16, 16);
+ int tv = sextract64(RttV, i * 16, 16);
+ xv = xv + tv;
+ sv = sv - tv;
+ PeV = deposit32(PeV, i * 2, 1, (xv > sv));
+ PeV = deposit32(PeV, i * 2 + 1, 1, (xv > sv));
}
- return x;
+ return PeV;
}
/*
@@ -332,8 +395,8 @@ static void check_noshuf(CPUHexagonState *env, uint32_t slot)
}
}
-static inline uint8_t mem_load1(CPUHexagonState *env, uint32_t slot,
- target_ulong vaddr)
+static uint8_t mem_load1(CPUHexagonState *env, uint32_t slot,
+ target_ulong vaddr)
{
uint8_t retval;
check_noshuf(env, slot);
@@ -341,8 +404,8 @@ static inline uint8_t mem_load1(CPUHexagonState *env, uint32_t slot,
return retval;
}
-static inline uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
- target_ulong vaddr)
+static uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
+ target_ulong vaddr)
{
uint16_t retval;
check_noshuf(env, slot);
@@ -350,8 +413,8 @@ static inline uint16_t mem_load2(CPUHexagonState *env, uint32_t slot,
return retval;
}
-static inline uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
- target_ulong vaddr)
+static uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
+ target_ulong vaddr)
{
uint32_t retval;
check_noshuf(env, slot);
@@ -359,8 +422,8 @@ static inline uint32_t mem_load4(CPUHexagonState *env, uint32_t slot,
return retval;
}
-static inline uint64_t mem_load8(CPUHexagonState *env, uint32_t slot,
- target_ulong vaddr)
+static uint64_t mem_load8(CPUHexagonState *env, uint32_t slot,
+ target_ulong vaddr)
{
uint64_t retval;
check_noshuf(env, slot);
@@ -374,7 +437,6 @@ float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV)
float64 out_f64;
arch_fpop_start(env);
out_f64 = float32_to_float64(RsV, &env->fp_status);
- out_f64 = hex_check_dfnan(out_f64);
arch_fpop_end(env);
return out_f64;
}
@@ -384,7 +446,6 @@ float32 HELPER(conv_df2sf)(CPUHexagonState *env, float64 RssV)
float32 out_f32;
arch_fpop_start(env);
out_f32 = float64_to_float32(RssV, &env->fp_status);
- out_f32 = hex_check_sfnan(out_f32);
arch_fpop_end(env);
return out_f32;
}
@@ -394,7 +455,6 @@ float32 HELPER(conv_uw2sf)(CPUHexagonState *env, int32_t RsV)
float32 RdV;
arch_fpop_start(env);
RdV = uint32_to_float32(RsV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -404,7 +464,6 @@ float64 HELPER(conv_uw2df)(CPUHexagonState *env, int32_t RsV)
float64 RddV;
arch_fpop_start(env);
RddV = uint32_to_float64(RsV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -414,7 +473,6 @@ float32 HELPER(conv_w2sf)(CPUHexagonState *env, int32_t RsV)
float32 RdV;
arch_fpop_start(env);
RdV = int32_to_float32(RsV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -424,7 +482,6 @@ float64 HELPER(conv_w2df)(CPUHexagonState *env, int32_t RsV)
float64 RddV;
arch_fpop_start(env);
RddV = int32_to_float64(RsV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -434,7 +491,6 @@ float32 HELPER(conv_ud2sf)(CPUHexagonState *env, int64_t RssV)
float32 RdV;
arch_fpop_start(env);
RdV = uint64_to_float32(RssV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -444,7 +500,6 @@ float64 HELPER(conv_ud2df)(CPUHexagonState *env, int64_t RssV)
float64 RddV;
arch_fpop_start(env);
RddV = uint64_to_float64(RssV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -454,7 +509,6 @@ float32 HELPER(conv_d2sf)(CPUHexagonState *env, int64_t RssV)
float32 RdV;
arch_fpop_start(env);
RdV = int64_to_float32(RssV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -464,16 +518,21 @@ float64 HELPER(conv_d2df)(CPUHexagonState *env, int64_t RssV)
float64 RddV;
arch_fpop_start(env);
RddV = int64_to_float64(RssV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
-int32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV)
+uint32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV)
{
- int32_t RdV;
+ uint32_t RdV;
arch_fpop_start(env);
- RdV = conv_sf_to_4u(RsV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = 0;
+ } else {
+ RdV = float32_to_uint32(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
@@ -482,16 +541,28 @@ int32_t HELPER(conv_sf2w)(CPUHexagonState *env, float32 RsV)
{
int32_t RdV;
arch_fpop_start(env);
- RdV = conv_sf_to_4s(RsV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = -1;
+ } else {
+ RdV = float32_to_int32(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
-int64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV)
+uint64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV)
{
- int64_t RddV;
+ uint64_t RddV;
arch_fpop_start(env);
- RddV = conv_sf_to_8u(RsV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = 0;
+ } else {
+ RddV = float32_to_uint64(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
@@ -500,16 +571,28 @@ int64_t HELPER(conv_sf2d)(CPUHexagonState *env, float32 RsV)
{
int64_t RddV;
arch_fpop_start(env);
- RddV = conv_sf_to_8s(RsV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = -1;
+ } else {
+ RddV = float32_to_int64(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
-int32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV)
+uint32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV)
{
- int32_t RdV;
+ uint32_t RdV;
arch_fpop_start(env);
- RdV = conv_df_to_4u(RssV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = 0;
+ } else {
+ RdV = float64_to_uint32(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
@@ -518,16 +601,28 @@ int32_t HELPER(conv_df2w)(CPUHexagonState *env, float64 RssV)
{
int32_t RdV;
arch_fpop_start(env);
- RdV = conv_df_to_4s(RssV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = -1;
+ } else {
+ RdV = float64_to_int32(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
-int64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV)
+uint64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV)
{
- int64_t RddV;
+ uint64_t RddV;
arch_fpop_start(env);
- RddV = conv_df_to_8u(RssV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = 0;
+ } else {
+ RddV = float64_to_uint64(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
@@ -536,17 +631,28 @@ int64_t HELPER(conv_df2d)(CPUHexagonState *env, float64 RssV)
{
int64_t RddV;
arch_fpop_start(env);
- RddV = conv_df_to_8s(RssV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = -1;
+ } else {
+ RddV = float64_to_int64(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
-int32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV)
+uint32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV)
{
- int32_t RdV;
+ uint32_t RdV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RdV = conv_sf_to_4u(RsV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = 0;
+ } else {
+ RdV = float32_to_uint32_round_to_zero(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
@@ -555,18 +661,28 @@ int32_t HELPER(conv_sf2w_chop)(CPUHexagonState *env, float32 RsV)
{
int32_t RdV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RdV = conv_sf_to_4s(RsV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = -1;
+ } else {
+ RdV = float32_to_int32_round_to_zero(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
-int64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV)
+uint64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV)
{
- int64_t RddV;
+ uint64_t RddV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RddV = conv_sf_to_8u(RsV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = 0;
+ } else {
+ RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
@@ -575,18 +691,28 @@ int64_t HELPER(conv_sf2d_chop)(CPUHexagonState *env, float32 RsV)
{
int64_t RddV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RddV = conv_sf_to_8s(RsV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float32_is_any_nan(RsV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = -1;
+ } else {
+ RddV = float32_to_int64_round_to_zero(RsV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
-int32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV)
+uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV)
{
- int32_t RdV;
+ uint32_t RdV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RdV = conv_df_to_4u(RssV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float64_is_neg(RssV) && !float32_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = 0;
+ } else {
+ RdV = float64_to_uint32_round_to_zero(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
@@ -595,18 +721,28 @@ int32_t HELPER(conv_df2w_chop)(CPUHexagonState *env, float64 RssV)
{
int32_t RdV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RdV = conv_df_to_4s(RssV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RdV = -1;
+ } else {
+ RdV = float64_to_int32_round_to_zero(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RdV;
}
-int64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV)
+uint64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV)
{
- int64_t RddV;
+ uint64_t RddV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RddV = conv_df_to_8u(RssV, &env->fp_status);
+ /* Hexagon checks the sign before rounding */
+ if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = 0;
+ } else {
+ RddV = float64_to_uint64_round_to_zero(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
@@ -615,8 +751,13 @@ int64_t HELPER(conv_df2d_chop)(CPUHexagonState *env, float64 RssV)
{
int64_t RddV;
arch_fpop_start(env);
- set_float_rounding_mode(float_round_to_zero, &env->fp_status);
- RddV = conv_df_to_8s(RssV, &env->fp_status);
+ /* Hexagon returns -1 for NaN */
+ if (float64_is_any_nan(RssV)) {
+ float_raise(float_flag_invalid, &env->fp_status);
+ RddV = -1;
+ } else {
+ RddV = float64_to_int64_round_to_zero(RssV, &env->fp_status);
+ }
arch_fpop_end(env);
return RddV;
}
@@ -626,7 +767,6 @@ float32 HELPER(sfadd)(CPUHexagonState *env, float32 RsV, float32 RtV)
float32 RdV;
arch_fpop_start(env);
RdV = float32_add(RsV, RtV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -636,7 +776,6 @@ float32 HELPER(sfsub)(CPUHexagonState *env, float32 RsV, float32 RtV)
float32 RdV;
arch_fpop_start(env);
RdV = float32_sub(RsV, RtV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -688,7 +827,6 @@ float32 HELPER(sfmax)(CPUHexagonState *env, float32 RsV, float32 RtV)
float32 RdV;
arch_fpop_start(env);
RdV = float32_maxnum(RsV, RtV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -698,7 +836,6 @@ float32 HELPER(sfmin)(CPUHexagonState *env, float32 RsV, float32 RtV)
float32 RdV;
arch_fpop_start(env);
RdV = float32_minnum(RsV, RtV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -765,7 +902,6 @@ float64 HELPER(dfadd)(CPUHexagonState *env, float64 RssV, float64 RttV)
float64 RddV;
arch_fpop_start(env);
RddV = float64_add(RssV, RttV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -775,7 +911,6 @@ float64 HELPER(dfsub)(CPUHexagonState *env, float64 RssV, float64 RttV)
float64 RddV;
arch_fpop_start(env);
RddV = float64_sub(RssV, RttV, &env->fp_status);
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -788,7 +923,6 @@ float64 HELPER(dfmax)(CPUHexagonState *env, float64 RssV, float64 RttV)
if (float64_is_any_nan(RssV) || float64_is_any_nan(RttV)) {
float_raise(float_flag_invalid, &env->fp_status);
}
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -801,7 +935,6 @@ float64 HELPER(dfmin)(CPUHexagonState *env, float64 RssV, float64 RttV)
if (float64_is_any_nan(RssV) || float64_is_any_nan(RttV)) {
float_raise(float_flag_invalid, &env->fp_status);
}
- RddV = hex_check_dfnan(RddV);
arch_fpop_end(env);
return RddV;
}
@@ -877,7 +1010,6 @@ float32 HELPER(sfmpy)(CPUHexagonState *env, float32 RsV, float32 RtV)
float32 RdV;
arch_fpop_start(env);
RdV = internal_mpyf(RsV, RtV, &env->fp_status);
- RdV = hex_check_sfnan(RdV);
arch_fpop_end(env);
return RdV;
}
@@ -887,7 +1019,6 @@ float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV,
{
arch_fpop_start(env);
RxV = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status);
- RxV = hex_check_sfnan(RxV);
arch_fpop_end(env);
return RxV;
}
@@ -919,7 +1050,6 @@ float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV,
RxV = check_nan(RxV, RsV, &env->fp_status);
RxV = check_nan(RxV, RtV, &env->fp_status);
tmp = internal_fmafx(RsV, RtV, RxV, fSXTN(8, 64, PuV), &env->fp_status);
- tmp = hex_check_sfnan(tmp);
if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) {
RxV = tmp;
}
@@ -934,12 +1064,11 @@ float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV,
arch_fpop_start(env);
neg_RsV = float32_sub(float32_zero, RsV, &env->fp_status);
RxV = internal_fmafx(neg_RsV, RtV, RxV, 0, &env->fp_status);
- RxV = hex_check_sfnan(RxV);
arch_fpop_end(env);
return RxV;
}
-static inline bool is_inf_prod(int32_t a, int32_t b)
+static bool is_inf_prod(int32_t a, int32_t b)
{
return (float32_is_infinity(a) && float32_is_infinity(b)) ||
(float32_is_infinity(a) && is_finite(b) && !float32_is_zero(b)) ||
@@ -949,8 +1078,8 @@ static inline bool is_inf_prod(int32_t a, int32_t b)
float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV,
float32 RsV, float32 RtV)
{
- int infinp;
- int infminusinf;
+ bool infinp;
+ bool infminusinf;
float32 tmp;
arch_fpop_start(env);
@@ -965,7 +1094,6 @@ float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV,
RxV = check_nan(RxV, RsV, &env->fp_status);
RxV = check_nan(RxV, RtV, &env->fp_status);
tmp = internal_fmafx(RsV, RtV, RxV, 0, &env->fp_status);
- tmp = hex_check_sfnan(tmp);
if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) {
RxV = tmp;
}
@@ -983,8 +1111,8 @@ float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV,
float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV,
float32 RsV, float32 RtV)
{
- int infinp;
- int infminusinf;
+ bool infinp;
+ bool infminusinf;
float32 tmp;
arch_fpop_start(env);
@@ -1000,7 +1128,6 @@ float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV,
RxV = check_nan(RxV, RtV, &env->fp_status);
float32 minus_RsV = float32_sub(float32_zero, RsV, &env->fp_status);
tmp = internal_fmafx(minus_RsV, RtV, RxV, 0, &env->fp_status);
- tmp = hex_check_sfnan(tmp);
if (!(float32_is_zero(RxV) && is_zero_prod(RsV, RtV))) {
RxV = tmp;
}
@@ -1024,13 +1151,11 @@ float64 HELPER(dfmpyfix)(CPUHexagonState *env, float64 RssV, float64 RttV)
float64_is_normal(RttV)) {
RddV = float64_mul(RssV, make_float64(0x4330000000000000),
&env->fp_status);
- RddV = hex_check_dfnan(RddV);
} else if (float64_is_denormal(RttV) &&
(float64_getexp(RssV) >= 512) &&
float64_is_normal(RssV)) {
RddV = float64_mul(RssV, make_float64(0x3cb0000000000000),
&env->fp_status);
- RddV = hex_check_dfnan(RddV);
} else {
RddV = RssV;
}
@@ -1043,7 +1168,6 @@ float64 HELPER(dfmpyhh)(CPUHexagonState *env, float64 RxxV,
{
arch_fpop_start(env);
RxxV = internal_mpyhh(RssV, RttV, RxxV, &env->fp_status);
- RxxV = hex_check_dfnan(RxxV);
arch_fpop_end(env);
return RxxV;
}
diff --git a/target/hexagon/reg_fields.c b/target/hexagon/reg_fields.c
index bdcab79428..6713203725 100644
--- a/target/hexagon/reg_fields.c
+++ b/target/hexagon/reg_fields.c
@@ -18,10 +18,9 @@
#include "qemu/osdep.h"
#include "reg_fields.h"
-const RegField reg_field_info[] = {
+const RegField reg_field_info[NUM_REG_FIELDS] = {
#define DEF_REG_FIELD(TAG, START, WIDTH) \
{ START, WIDTH },
#include "reg_fields_def.h.inc"
- { 0, 0 }
#undef DEF_REG_FIELD
};
diff --git a/target/hexagon/reg_fields.h b/target/hexagon/reg_fields.h
index d3c86c942f..9e2ad5d997 100644
--- a/target/hexagon/reg_fields.h
+++ b/target/hexagon/reg_fields.h
@@ -23,8 +23,6 @@ typedef struct {
int width;
} RegField;
-extern const RegField reg_field_info[];
-
enum {
#define DEF_REG_FIELD(TAG, START, WIDTH) \
TAG,
@@ -33,4 +31,6 @@ enum {
#undef DEF_REG_FIELD
};
+extern const RegField reg_field_info[NUM_REG_FIELDS];
+
#endif
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index eeaad5f8ba..9a37644182 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -35,9 +35,7 @@ TCGv hex_this_PC;
TCGv hex_slot_cancelled;
TCGv hex_branch_taken;
TCGv hex_new_value[TOTAL_PER_THREAD_REGS];
-#if HEX_DEBUG
TCGv hex_reg_written[TOTAL_PER_THREAD_REGS];
-#endif
TCGv hex_new_pred_value[NUM_PREGS];
TCGv hex_pred_written;
TCGv hex_store_addr[STORES_MAX];
@@ -54,19 +52,42 @@ static const char * const hexagon_prednames[] = {
"p0", "p1", "p2", "p3"
};
-void gen_exception(int excp)
+static void gen_exception_raw(int excp)
{
TCGv_i32 helper_tmp = tcg_const_i32(excp);
gen_helper_raise_exception(cpu_env, helper_tmp);
tcg_temp_free_i32(helper_tmp);
}
-void gen_exception_debug(void)
+static void gen_exec_counters(DisasContext *ctx)
{
- gen_exception(EXCP_DEBUG);
+ tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_PKT_CNT],
+ hex_gpr[HEX_REG_QEMU_PKT_CNT], ctx->num_packets);
+ tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_INSN_CNT],
+ hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns);
+}
+
+static void gen_end_tb(DisasContext *ctx)
+{
+ gen_exec_counters(ctx);
+ tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
+ if (ctx->base.singlestep_enabled) {
+ gen_exception_raw(EXCP_DEBUG);
+ } else {
+ tcg_gen_exit_tb(NULL, 0);
+ }
+ ctx->base.is_jmp = DISAS_NORETURN;
+}
+
+static void gen_exception_end_tb(DisasContext *ctx, int excp)
+{
+ gen_exec_counters(ctx);
+ tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
+ gen_exception_raw(excp);
+ ctx->base.is_jmp = DISAS_NORETURN;
+
}
-#if HEX_DEBUG
#define PACKET_BUFFER_LEN 1028
static void print_pkt(Packet *pkt)
{
@@ -75,10 +96,12 @@ static void print_pkt(Packet *pkt)
HEX_DEBUG_LOG("%s", buf->str);
g_string_free(buf, true);
}
-#define HEX_DEBUG_PRINT_PKT(pkt) print_pkt(pkt)
-#else
-#define HEX_DEBUG_PRINT_PKT(pkt) /* nothing */
-#endif
+#define HEX_DEBUG_PRINT_PKT(pkt) \
+ do { \
+ if (HEX_DEBUG) { \
+ print_pkt(pkt); \
+ } \
+ } while (0)
static int read_packet_words(CPUHexagonState *env, DisasContext *ctx,
uint32_t words[])
@@ -88,8 +111,8 @@ static int read_packet_words(CPUHexagonState *env, DisasContext *ctx,
memset(words, 0, PACKET_WORDS_MAX * sizeof(uint32_t));
for (nwords = 0; !found_end && nwords < PACKET_WORDS_MAX; nwords++) {
- words[nwords] = cpu_ldl_code(env,
- ctx->base.pc_next + nwords * sizeof(uint32_t));
+ words[nwords] =
+ translator_ldl(env, ctx->base.pc_next + nwords * sizeof(uint32_t));
found_end = is_packet_end(words[nwords]);
}
if (!found_end) {
@@ -148,17 +171,18 @@ static void gen_start_packet(DisasContext *ctx, Packet *pkt)
ctx->reg_log_idx = 0;
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
ctx->preg_log_idx = 0;
+ bitmap_zero(ctx->pregs_written, NUM_PREGS);
for (i = 0; i < STORES_MAX; i++) {
ctx->store_width[i] = 0;
}
tcg_gen_movi_tl(hex_pkt_has_store_s1, pkt->pkt_has_store_s1);
- ctx->s1_store_processed = 0;
+ ctx->s1_store_processed = false;
-#if HEX_DEBUG
- /* Handy place to set a breakpoint before the packet executes */
- gen_helper_debug_start_packet(cpu_env);
- tcg_gen_movi_tl(hex_this_PC, ctx->base.pc_next);
-#endif
+ if (HEX_DEBUG) {
+ /* Handy place to set a breakpoint before the packet executes */
+ gen_helper_debug_start_packet(cpu_env);
+ tcg_gen_movi_tl(hex_this_PC, ctx->base.pc_next);
+ }
/* Initialize the runtime state for packet semantics */
if (need_pc(pkt)) {
@@ -185,7 +209,7 @@ static void mark_implicit_reg_write(DisasContext *ctx, Insn *insn,
int attrib, int rnum)
{
if (GET_ATTRIB(insn->opcode, attrib)) {
- int is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC);
+ bool is_predicated = GET_ATTRIB(insn->opcode, A_CONDEXEC);
if (is_predicated && !is_preloaded(ctx, rnum)) {
tcg_gen_mov_tl(hex_new_value[rnum], hex_gpr[rnum]);
}
@@ -202,7 +226,7 @@ static void mark_implicit_pred_write(DisasContext *ctx, Insn *insn,
}
}
-static void mark_implicit_writes(DisasContext *ctx, Insn *insn)
+static void mark_implicit_reg_writes(DisasContext *ctx, Insn *insn)
{
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_FP, HEX_REG_FP);
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SP, HEX_REG_SP);
@@ -211,7 +235,10 @@ static void mark_implicit_writes(DisasContext *ctx, Insn *insn)
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA0, HEX_REG_SA0);
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_LC1, HEX_REG_LC1);
mark_implicit_reg_write(ctx, insn, A_IMPLICIT_WRITES_SA1, HEX_REG_SA1);
+}
+static void mark_implicit_pred_writes(DisasContext *ctx, Insn *insn)
+{
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P0, 0);
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P1, 1);
mark_implicit_pred_write(ctx, insn, A_IMPLICIT_WRITES_P2, 2);
@@ -222,11 +249,11 @@ static void gen_insn(CPUHexagonState *env, DisasContext *ctx,
Insn *insn, Packet *pkt)
{
if (insn->generate) {
- mark_implicit_writes(ctx, insn);
+ mark_implicit_reg_writes(ctx, insn);
insn->generate(env, ctx, insn, pkt);
+ mark_implicit_pred_writes(ctx, insn);
} else {
- gen_exception(HEX_EXCP_INVALID_OPCODE);
- ctx->base.is_jmp = DISAS_NORETURN;
+ gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE);
}
}
@@ -280,10 +307,11 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt)
for (i = 0; i < ctx->preg_log_idx; i++) {
int pred_num = ctx->preg_log[i];
tcg_gen_mov_tl(hex_pred[pred_num], hex_new_pred_value[pred_num]);
-#if HEX_DEBUG
- /* Do this so HELPER(debug_commit_end) will know */
- tcg_gen_ori_tl(hex_pred_written, hex_pred_written, 1 << pred_num);
-#endif
+ if (HEX_DEBUG) {
+ /* Do this so HELPER(debug_commit_end) will know */
+ tcg_gen_ori_tl(hex_pred_written, hex_pred_written,
+ 1 << pred_num);
+ }
}
}
@@ -292,20 +320,16 @@ static void gen_pred_writes(DisasContext *ctx, Packet *pkt)
tcg_temp_free(pval);
}
-#if HEX_DEBUG
-static inline void gen_check_store_width(DisasContext *ctx, int slot_num)
+static void gen_check_store_width(DisasContext *ctx, int slot_num)
{
- TCGv slot = tcg_const_tl(slot_num);
- TCGv check = tcg_const_tl(ctx->store_width[slot_num]);
- gen_helper_debug_check_store_width(cpu_env, slot, check);
- tcg_temp_free(slot);
- tcg_temp_free(check);
+ if (HEX_DEBUG) {
+ TCGv slot = tcg_const_tl(slot_num);
+ TCGv check = tcg_const_tl(ctx->store_width[slot_num]);
+ gen_helper_debug_check_store_width(cpu_env, slot, check);
+ tcg_temp_free(slot);
+ tcg_temp_free(check);
+ }
}
-#define HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num) \
- gen_check_store_width(ctx, slot_num)
-#else
-#define HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num) /* nothing */
-#endif
static bool slot_is_predicated(Packet *pkt, int slot_num)
{
@@ -330,7 +354,7 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num)
if (slot_num == 1 && ctx->s1_store_processed) {
return;
}
- ctx->s1_store_processed = 1;
+ ctx->s1_store_processed = true;
if (is_predicated) {
TCGv cancelled = tcg_temp_new();
@@ -355,25 +379,25 @@ void process_store(DisasContext *ctx, Packet *pkt, int slot_num)
*/
switch (ctx->store_width[slot_num]) {
case 1:
- HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num);
+ gen_check_store_width(ctx, slot_num);
tcg_gen_qemu_st8(hex_store_val32[slot_num],
hex_store_addr[slot_num],
ctx->mem_idx);
break;
case 2:
- HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num);
+ gen_check_store_width(ctx, slot_num);
tcg_gen_qemu_st16(hex_store_val32[slot_num],
hex_store_addr[slot_num],
ctx->mem_idx);
break;
case 4:
- HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num);
+ gen_check_store_width(ctx, slot_num);
tcg_gen_qemu_st32(hex_store_val32[slot_num],
hex_store_addr[slot_num],
ctx->mem_idx);
break;
case 8:
- HEX_DEBUG_GEN_CHECK_STORE_WIDTH(ctx, slot_num);
+ gen_check_store_width(ctx, slot_num);
tcg_gen_qemu_st64(hex_store_val64[slot_num],
hex_store_addr[slot_num],
ctx->mem_idx);
@@ -451,14 +475,6 @@ static void update_exec_counters(DisasContext *ctx, Packet *pkt)
ctx->num_insns += num_real_insns;
}
-static void gen_exec_counters(DisasContext *ctx)
-{
- tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_PKT_CNT],
- hex_gpr[HEX_REG_QEMU_PKT_CNT], ctx->num_packets);
- tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_INSN_CNT],
- hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns);
-}
-
static void gen_commit_packet(DisasContext *ctx, Packet *pkt)
{
gen_reg_writes(ctx);
@@ -466,8 +482,7 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt)
process_store_log(ctx, pkt);
process_dczeroa(ctx, pkt);
update_exec_counters(ctx, pkt);
-#if HEX_DEBUG
- {
+ if (HEX_DEBUG) {
TCGv has_st0 =
tcg_const_tl(pkt->pkt_has_store_s0 && !pkt->pkt_has_dczeroa);
TCGv has_st1 =
@@ -479,10 +494,9 @@ static void gen_commit_packet(DisasContext *ctx, Packet *pkt)
tcg_temp_free(has_st0);
tcg_temp_free(has_st1);
}
-#endif
if (pkt->pkt_has_cof) {
- ctx->base.is_jmp = DISAS_NORETURN;
+ gen_end_tb(ctx);
}
}
@@ -495,8 +509,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx)
nwords = read_packet_words(env, ctx, words);
if (!nwords) {
- gen_exception(HEX_EXCP_INVALID_PACKET);
- ctx->base.is_jmp = DISAS_NORETURN;
+ gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET);
return;
}
@@ -509,8 +522,7 @@ static void decode_and_translate_packet(CPUHexagonState *env, DisasContext *ctx)
gen_commit_packet(ctx, &pkt);
ctx->base.pc_next += pkt.encod_pkt_size_in_bytes;
} else {
- gen_exception(HEX_EXCP_INVALID_PACKET);
- ctx->base.is_jmp = DISAS_NORETURN;
+ gen_exception_end_tb(ctx, HEX_EXCP_INVALID_PACKET);
}
}
@@ -540,9 +552,7 @@ static bool hexagon_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
- tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
- ctx->base.is_jmp = DISAS_NORETURN;
- gen_exception_debug();
+ gen_exception_end_tb(ctx, EXCP_DEBUG);
/*
* The address covered by the breakpoint must be included in
* [tb->pc, tb->pc + tb->size) in order to for it to be
@@ -589,14 +599,10 @@ static void hexagon_tr_translate_packet(DisasContextBase *dcbase, CPUState *cpu)
* The CPU log is used to compare against LLDB single stepping,
* so end the TLB after every packet.
*/
- HexagonCPU *hex_cpu = container_of(env, HexagonCPU, env);
+ HexagonCPU *hex_cpu = env_archcpu(env);
if (hex_cpu->lldb_compat && qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
ctx->base.is_jmp = DISAS_TOO_MANY;
}
-#if HEX_DEBUG
- /* When debugging, only put one packet per TB */
- ctx->base.is_jmp = DISAS_TOO_MANY;
-#endif
}
}
@@ -609,19 +615,12 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
gen_exec_counters(ctx);
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
if (ctx->base.singlestep_enabled) {
- gen_exception_debug();
+ gen_exception_raw(EXCP_DEBUG);
} else {
tcg_gen_exit_tb(NULL, 0);
}
break;
case DISAS_NORETURN:
- gen_exec_counters(ctx);
- tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC);
- if (ctx->base.singlestep_enabled) {
- gen_exception_debug();
- } else {
- tcg_gen_exit_tb(NULL, 0);
- }
break;
default:
g_assert_not_reached();
@@ -654,9 +653,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
#define NAME_LEN 64
static char new_value_names[TOTAL_PER_THREAD_REGS][NAME_LEN];
-#if HEX_DEBUG
static char reg_written_names[TOTAL_PER_THREAD_REGS][NAME_LEN];
-#endif
static char new_pred_value_names[NUM_PREGS][NAME_LEN];
static char store_addr_names[STORES_MAX][NAME_LEN];
static char store_width_names[STORES_MAX][NAME_LEN];
@@ -669,11 +666,11 @@ void hexagon_translate_init(void)
opcode_init();
-#if HEX_DEBUG
- if (!qemu_logfile) {
- qemu_set_log(qemu_loglevel);
+ if (HEX_DEBUG) {
+ if (!qemu_logfile) {
+ qemu_set_log(qemu_loglevel);
+ }
}
-#endif
for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) {
hex_gpr[i] = tcg_global_mem_new(cpu_env,
@@ -685,13 +682,13 @@ void hexagon_translate_init(void)
offsetof(CPUHexagonState, new_value[i]),
new_value_names[i]);
-#if HEX_DEBUG
- snprintf(reg_written_names[i], NAME_LEN, "reg_written_%s",
- hexagon_regnames[i]);
- hex_reg_written[i] = tcg_global_mem_new(cpu_env,
- offsetof(CPUHexagonState, reg_written[i]),
- reg_written_names[i]);
-#endif
+ if (HEX_DEBUG) {
+ snprintf(reg_written_names[i], NAME_LEN, "reg_written_%s",
+ hexagon_regnames[i]);
+ hex_reg_written[i] = tcg_global_mem_new(cpu_env,
+ offsetof(CPUHexagonState, reg_written[i]),
+ reg_written_names[i]);
+ }
}
for (i = 0; i < NUM_PREGS; i++) {
hex_pred[i] = tcg_global_mem_new(cpu_env,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index 938f7fbb9f..703fd1345f 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -34,17 +34,16 @@ typedef struct DisasContext {
DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS);
int preg_log[PRED_WRITES_MAX];
int preg_log_idx;
+ DECLARE_BITMAP(pregs_written, NUM_PREGS);
uint8_t store_width[STORES_MAX];
- uint8_t s1_store_processed;
+ bool s1_store_processed;
} DisasContext;
static inline void ctx_log_reg_write(DisasContext *ctx, int rnum)
{
-#if HEX_DEBUG
if (test_bit(rnum, ctx->regs_written)) {
HEX_DEBUG_LOG("WARNING: Multiple writes to r%d\n", rnum);
}
-#endif
ctx->reg_log[ctx->reg_log_idx] = rnum;
ctx->reg_log_idx++;
set_bit(rnum, ctx->regs_written);
@@ -60,6 +59,7 @@ static inline void ctx_log_pred_write(DisasContext *ctx, int pnum)
{
ctx->preg_log[ctx->preg_log_idx] = pnum;
ctx->preg_log_idx++;
+ set_bit(pnum, ctx->pregs_written);
}
static inline bool is_preloaded(DisasContext *ctx, int num)
@@ -86,8 +86,5 @@ extern TCGv hex_llsc_addr;
extern TCGv hex_llsc_val;
extern TCGv_i64 hex_llsc_val_i64;
-void gen_exception(int excp);
-void gen_exception_debug(void);
-
void process_store(DisasContext *ctx, Packet *pkt, int slot_num);
#endif
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index dce1e166bd..1ad2fe4aa3 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -35,155 +35,84 @@
#include "qapi/qapi-commands-machine-target.h"
#include "fpu_helper.h"
-#if !defined(CONFIG_USER_ONLY)
+const char regnames[32][4] = {
+ "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
+};
-/* Called for updates to CP0_Status. */
-void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
+static void fpu_dump_fpr(fpr_t *fpr, FILE *f, bool is_fpu64)
{
- int32_t tcstatus, *tcst;
- uint32_t v = cpu->CP0_Status;
- uint32_t cu, mx, asid, ksu;
- uint32_t mask = ((1 << CP0TCSt_TCU3)
- | (1 << CP0TCSt_TCU2)
- | (1 << CP0TCSt_TCU1)
- | (1 << CP0TCSt_TCU0)
- | (1 << CP0TCSt_TMX)
- | (3 << CP0TCSt_TKSU)
- | (0xff << CP0TCSt_TASID));
-
- cu = (v >> CP0St_CU0) & 0xf;
- mx = (v >> CP0St_MX) & 0x1;
- ksu = (v >> CP0St_KSU) & 0x3;
- asid = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
-
- tcstatus = cu << CP0TCSt_TCU0;
- tcstatus |= mx << CP0TCSt_TMX;
- tcstatus |= ksu << CP0TCSt_TKSU;
- tcstatus |= asid;
-
- if (tc == cpu->current_tc) {
- tcst = &cpu->active_tc.CP0_TCStatus;
+ if (is_fpu64) {
+ qemu_fprintf(f, "w:%08x d:%016" PRIx64 " fd:%13g fs:%13g psu: %13g\n",
+ fpr->w[FP_ENDIAN_IDX], fpr->d,
+ (double)fpr->fd,
+ (double)fpr->fs[FP_ENDIAN_IDX],
+ (double)fpr->fs[!FP_ENDIAN_IDX]);
} else {
- tcst = &cpu->tcs[tc].CP0_TCStatus;
- }
+ fpr_t tmp;
- *tcst &= ~mask;
- *tcst |= tcstatus;
- compute_hflags(cpu);
+ tmp.w[FP_ENDIAN_IDX] = fpr->w[FP_ENDIAN_IDX];
+ tmp.w[!FP_ENDIAN_IDX] = (fpr + 1)->w[FP_ENDIAN_IDX];
+ qemu_fprintf(f, "w:%08x d:%016" PRIx64 " fd:%13g fs:%13g psu:%13g\n",
+ tmp.w[FP_ENDIAN_IDX], tmp.d,
+ (double)tmp.fd,
+ (double)tmp.fs[FP_ENDIAN_IDX],
+ (double)tmp.fs[!FP_ENDIAN_IDX]);
+ }
}
-void cpu_mips_store_status(CPUMIPSState *env, target_ulong val)
+static void fpu_dump_state(CPUMIPSState *env, FILE *f, int flags)
{
- uint32_t mask = env->CP0_Status_rw_bitmask;
- target_ulong old = env->CP0_Status;
-
- if (env->insn_flags & ISA_MIPS_R6) {
- bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3;
-#if defined(TARGET_MIPS64)
- uint32_t ksux = (1 << CP0St_KX) & val;
- ksux |= (ksux >> 1) & val; /* KX = 0 forces SX to be 0 */
- ksux |= (ksux >> 1) & val; /* SX = 0 forces UX to be 0 */
- val = (val & ~(7 << CP0St_UX)) | ksux;
-#endif
- if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) {
- mask &= ~(3 << CP0St_KSU);
- }
- mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val);
- }
+ int i;
+ bool is_fpu64 = !!(env->hflags & MIPS_HFLAG_F64);
- env->CP0_Status = (old & ~mask) | (val & mask);
-#if defined(TARGET_MIPS64)
- if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) {
- /* Access to at least one of the 64-bit segments has been disabled */
- tlb_flush(env_cpu(env));
- }
-#endif
- if (ase_mt_available(env)) {
- sync_c0_status(env, env, env->current_tc);
- } else {
- compute_hflags(env);
+ qemu_fprintf(f,
+ "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%02x\n",
+ env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64,
+ get_float_exception_flags(&env->active_fpu.fp_status));
+ for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) {
+ qemu_fprintf(f, "%3s: ", fregnames[i]);
+ fpu_dump_fpr(&env->active_fpu.fpr[i], f, is_fpu64);
}
}
-void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val)
+static void mips_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
- uint32_t mask = 0x00C00300;
- uint32_t old = env->CP0_Cause;
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
int i;
- if (env->insn_flags & ISA_MIPS_R2) {
- mask |= 1 << CP0Ca_DC;
- }
- if (env->insn_flags & ISA_MIPS_R6) {
- mask &= ~((1 << CP0Ca_WP) & val);
- }
-
- env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask);
-
- if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) {
- if (env->CP0_Cause & (1 << CP0Ca_DC)) {
- cpu_mips_stop_count(env);
- } else {
- cpu_mips_start_count(env);
+ qemu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx
+ " LO=0x" TARGET_FMT_lx " ds %04x "
+ TARGET_FMT_lx " " TARGET_FMT_ld "\n",
+ env->active_tc.PC, env->active_tc.HI[0], env->active_tc.LO[0],
+ env->hflags, env->btarget, env->bcond);
+ for (i = 0; i < 32; i++) {
+ if ((i & 3) == 0) {
+ qemu_fprintf(f, "GPR%02d:", i);
}
- }
-
- /* Set/reset software interrupts */
- for (i = 0 ; i < 2 ; i++) {
- if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
- cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+ qemu_fprintf(f, " %s " TARGET_FMT_lx,
+ regnames[i], env->active_tc.gpr[i]);
+ if ((i & 3) == 3) {
+ qemu_fprintf(f, "\n");
}
}
-}
-#endif /* !CONFIG_USER_ONLY */
-
-static const char * const excp_names[EXCP_LAST + 1] = {
- [EXCP_RESET] = "reset",
- [EXCP_SRESET] = "soft reset",
- [EXCP_DSS] = "debug single step",
- [EXCP_DINT] = "debug interrupt",
- [EXCP_NMI] = "non-maskable interrupt",
- [EXCP_MCHECK] = "machine check",
- [EXCP_EXT_INTERRUPT] = "interrupt",
- [EXCP_DFWATCH] = "deferred watchpoint",
- [EXCP_DIB] = "debug instruction breakpoint",
- [EXCP_IWATCH] = "instruction fetch watchpoint",
- [EXCP_AdEL] = "address error load",
- [EXCP_AdES] = "address error store",
- [EXCP_TLBF] = "TLB refill",
- [EXCP_IBE] = "instruction bus error",
- [EXCP_DBp] = "debug breakpoint",
- [EXCP_SYSCALL] = "syscall",
- [EXCP_BREAK] = "break",
- [EXCP_CpU] = "coprocessor unusable",
- [EXCP_RI] = "reserved instruction",
- [EXCP_OVERFLOW] = "arithmetic overflow",
- [EXCP_TRAP] = "trap",
- [EXCP_FPE] = "floating point",
- [EXCP_DDBS] = "debug data break store",
- [EXCP_DWATCH] = "data watchpoint",
- [EXCP_LTLBL] = "TLB modify",
- [EXCP_TLBL] = "TLB load",
- [EXCP_TLBS] = "TLB store",
- [EXCP_DBE] = "data bus error",
- [EXCP_DDBL] = "debug data break load",
- [EXCP_THREAD] = "thread",
- [EXCP_MDMX] = "MDMX",
- [EXCP_C2E] = "precise coprocessor 2",
- [EXCP_CACHE] = "cache error",
- [EXCP_TLBXI] = "TLB execute-inhibit",
- [EXCP_TLBRI] = "TLB read-inhibit",
- [EXCP_MSADIS] = "MSA disabled",
- [EXCP_MSAFPE] = "MSA floating point",
-};
-
-const char *mips_exception_name(int32_t exception)
-{
- if (exception < 0 || exception > EXCP_LAST) {
- return "unknown";
+ qemu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x"
+ TARGET_FMT_lx "\n",
+ env->CP0_Status, env->CP0_Cause, env->CP0_EPC);
+ qemu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%016"
+ PRIx64 "\n",
+ env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr);
+ qemu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n",
+ env->CP0_Config2, env->CP0_Config3);
+ qemu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n",
+ env->CP0_Config4, env->CP0_Config5);
+ if ((flags & CPU_DUMP_FPU) && (env->hflags & MIPS_HFLAG_FPU)) {
+ fpu_dump_state(env, f, flags);
}
- return excp_names[exception];
}
void cpu_set_exception_base(int vp_index, target_ulong address)
@@ -192,101 +121,13 @@ void cpu_set_exception_base(int vp_index, target_ulong address)
vp->env.exception_base = address;
}
-target_ulong exception_resume_pc(CPUMIPSState *env)
-{
- target_ulong bad_pc;
- target_ulong isa_mode;
-
- isa_mode = !!(env->hflags & MIPS_HFLAG_M16);
- bad_pc = env->active_tc.PC | isa_mode;
- if (env->hflags & MIPS_HFLAG_BMASK) {
- /*
- * If the exception was raised from a delay slot, come back to
- * the jump.
- */
- bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
- }
-
- return bad_pc;
-}
-
-bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
-{
- if (interrupt_request & CPU_INTERRUPT_HARD) {
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
-
- if (cpu_mips_hw_interrupts_enabled(env) &&
- cpu_mips_hw_interrupts_pending(env)) {
- /* Raise it */
- cs->exception_index = EXCP_EXT_INTERRUPT;
- env->error_code = 0;
- mips_cpu_do_interrupt(cs);
- return true;
- }
- }
- return false;
-}
-
-void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env,
- uint32_t exception,
- int error_code,
- uintptr_t pc)
-{
- CPUState *cs = env_cpu(env);
-
- qemu_log_mask(CPU_LOG_INT, "%s: %d (%s) %d\n",
- __func__, exception, mips_exception_name(exception),
- error_code);
- cs->exception_index = exception;
- env->error_code = error_code;
-
- cpu_loop_exit_restore(cs, pc);
-}
-
static void mips_cpu_set_pc(CPUState *cs, vaddr value)
{
MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
- env->active_tc.PC = value & ~(target_ulong)1;
- if (value & 1) {
- env->hflags |= MIPS_HFLAG_M16;
- } else {
- env->hflags &= ~(MIPS_HFLAG_M16);
- }
+ mips_env_set_pc(&cpu->env, value);
}
-#ifdef CONFIG_TCG
-static void mips_cpu_synchronize_from_tb(CPUState *cs,
- const TranslationBlock *tb)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
-
- env->active_tc.PC = tb->pc;
- env->hflags &= ~MIPS_HFLAG_BMASK;
- env->hflags |= tb->flags & MIPS_HFLAG_BMASK;
-}
-
-# ifndef CONFIG_USER_ONLY
-static bool mips_io_recompile_replay_branch(CPUState *cs,
- const TranslationBlock *tb)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
-
- if ((env->hflags & MIPS_HFLAG_BMASK) != 0
- && env->active_tc.PC != tb->pc) {
- env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
- env->hflags &= ~MIPS_HFLAG_BMASK;
- return true;
- }
- return false;
-}
-# endif /* !CONFIG_USER_ONLY */
-#endif /* CONFIG_TCG */
-
static bool mips_cpu_has_work(CPUState *cs)
{
MIPSCPU *cpu = MIPS_CPU(cs);
@@ -634,7 +475,7 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
env->exception_base = (int32_t)0xBFC00000;
-#ifndef CONFIG_USER_ONLY
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
mmu_init(env, env->cpu_model);
#endif
fpu_init(env, env->cpu_model);
diff --git a/target/mips/fpu.c b/target/mips/fpu.c
new file mode 100644
index 0000000000..c7c487c1f9
--- /dev/null
+++ b/target/mips/fpu.c
@@ -0,0 +1,25 @@
+/*
+ * Helpers for emulation of FPU-related MIPS instructions.
+ *
+ * Copyright (C) 2004-2005 Jocelyn Mayer
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+#include "qemu/osdep.h"
+#include "fpu/softfloat-helpers.h"
+#include "fpu_helper.h"
+
+/* convert MIPS rounding mode in FCR31 to IEEE library */
+const FloatRoundMode ieee_rm[4] = {
+ float_round_nearest_even,
+ float_round_to_zero,
+ float_round_up,
+ float_round_down
+};
+
+const char fregnames[32][4] = {
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+};
diff --git a/target/mips/helper.h b/target/mips/helper.h
index 709494445d..a9c6c7d1a3 100644
--- a/target/mips/helper.h
+++ b/target/mips/helper.h
@@ -2,10 +2,6 @@ DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int)
DEF_HELPER_2(raise_exception, noreturn, env, i32)
DEF_HELPER_1(raise_exception_debug, noreturn, env)
-#ifndef CONFIG_USER_ONLY
-DEF_HELPER_1(do_semihosting, void, env)
-#endif
-
#ifdef TARGET_MIPS64
DEF_HELPER_4(sdl, void, env, tl, tl, int)
DEF_HELPER_4(sdr, void, env, tl, tl, int)
@@ -42,164 +38,6 @@ DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32)
-#ifndef CONFIG_USER_ONLY
-/* CP0 helpers */
-DEF_HELPER_1(mfc0_mvpcontrol, tl, env)
-DEF_HELPER_1(mfc0_mvpconf0, tl, env)
-DEF_HELPER_1(mfc0_mvpconf1, tl, env)
-DEF_HELPER_1(mftc0_vpecontrol, tl, env)
-DEF_HELPER_1(mftc0_vpeconf0, tl, env)
-DEF_HELPER_1(mfc0_random, tl, env)
-DEF_HELPER_1(mfc0_tcstatus, tl, env)
-DEF_HELPER_1(mftc0_tcstatus, tl, env)
-DEF_HELPER_1(mfc0_tcbind, tl, env)
-DEF_HELPER_1(mftc0_tcbind, tl, env)
-DEF_HELPER_1(mfc0_tcrestart, tl, env)
-DEF_HELPER_1(mftc0_tcrestart, tl, env)
-DEF_HELPER_1(mfc0_tchalt, tl, env)
-DEF_HELPER_1(mftc0_tchalt, tl, env)
-DEF_HELPER_1(mfc0_tccontext, tl, env)
-DEF_HELPER_1(mftc0_tccontext, tl, env)
-DEF_HELPER_1(mfc0_tcschedule, tl, env)
-DEF_HELPER_1(mftc0_tcschedule, tl, env)
-DEF_HELPER_1(mfc0_tcschefback, tl, env)
-DEF_HELPER_1(mftc0_tcschefback, tl, env)
-DEF_HELPER_1(mfc0_count, tl, env)
-DEF_HELPER_1(mfc0_saar, tl, env)
-DEF_HELPER_1(mfhc0_saar, tl, env)
-DEF_HELPER_1(mftc0_entryhi, tl, env)
-DEF_HELPER_1(mftc0_status, tl, env)
-DEF_HELPER_1(mftc0_cause, tl, env)
-DEF_HELPER_1(mftc0_epc, tl, env)
-DEF_HELPER_1(mftc0_ebase, tl, env)
-DEF_HELPER_2(mftc0_configx, tl, env, tl)
-DEF_HELPER_1(mfc0_lladdr, tl, env)
-DEF_HELPER_1(mfc0_maar, tl, env)
-DEF_HELPER_1(mfhc0_maar, tl, env)
-DEF_HELPER_2(mfc0_watchlo, tl, env, i32)
-DEF_HELPER_2(mfc0_watchhi, tl, env, i32)
-DEF_HELPER_2(mfhc0_watchhi, tl, env, i32)
-DEF_HELPER_1(mfc0_debug, tl, env)
-DEF_HELPER_1(mftc0_debug, tl, env)
-#ifdef TARGET_MIPS64
-DEF_HELPER_1(dmfc0_tcrestart, tl, env)
-DEF_HELPER_1(dmfc0_tchalt, tl, env)
-DEF_HELPER_1(dmfc0_tccontext, tl, env)
-DEF_HELPER_1(dmfc0_tcschedule, tl, env)
-DEF_HELPER_1(dmfc0_tcschefback, tl, env)
-DEF_HELPER_1(dmfc0_lladdr, tl, env)
-DEF_HELPER_1(dmfc0_maar, tl, env)
-DEF_HELPER_2(dmfc0_watchlo, tl, env, i32)
-DEF_HELPER_2(dmfc0_watchhi, tl, env, i32)
-DEF_HELPER_1(dmfc0_saar, tl, env)
-#endif /* TARGET_MIPS64 */
-
-DEF_HELPER_2(mtc0_index, void, env, tl)
-DEF_HELPER_2(mtc0_mvpcontrol, void, env, tl)
-DEF_HELPER_2(mtc0_vpecontrol, void, env, tl)
-DEF_HELPER_2(mttc0_vpecontrol, void, env, tl)
-DEF_HELPER_2(mtc0_vpeconf0, void, env, tl)
-DEF_HELPER_2(mttc0_vpeconf0, void, env, tl)
-DEF_HELPER_2(mtc0_vpeconf1, void, env, tl)
-DEF_HELPER_2(mtc0_yqmask, void, env, tl)
-DEF_HELPER_2(mtc0_vpeopt, void, env, tl)
-DEF_HELPER_2(mtc0_entrylo0, void, env, tl)
-DEF_HELPER_2(mtc0_tcstatus, void, env, tl)
-DEF_HELPER_2(mttc0_tcstatus, void, env, tl)
-DEF_HELPER_2(mtc0_tcbind, void, env, tl)
-DEF_HELPER_2(mttc0_tcbind, void, env, tl)
-DEF_HELPER_2(mtc0_tcrestart, void, env, tl)
-DEF_HELPER_2(mttc0_tcrestart, void, env, tl)
-DEF_HELPER_2(mtc0_tchalt, void, env, tl)
-DEF_HELPER_2(mttc0_tchalt, void, env, tl)
-DEF_HELPER_2(mtc0_tccontext, void, env, tl)
-DEF_HELPER_2(mttc0_tccontext, void, env, tl)
-DEF_HELPER_2(mtc0_tcschedule, void, env, tl)
-DEF_HELPER_2(mttc0_tcschedule, void, env, tl)
-DEF_HELPER_2(mtc0_tcschefback, void, env, tl)
-DEF_HELPER_2(mttc0_tcschefback, void, env, tl)
-DEF_HELPER_2(mtc0_entrylo1, void, env, tl)
-DEF_HELPER_2(mtc0_context, void, env, tl)
-DEF_HELPER_2(mtc0_memorymapid, void, env, tl)
-DEF_HELPER_2(mtc0_pagemask, void, env, tl)
-DEF_HELPER_2(mtc0_pagegrain, void, env, tl)
-DEF_HELPER_2(mtc0_segctl0, void, env, tl)
-DEF_HELPER_2(mtc0_segctl1, void, env, tl)
-DEF_HELPER_2(mtc0_segctl2, void, env, tl)
-DEF_HELPER_2(mtc0_pwfield, void, env, tl)
-DEF_HELPER_2(mtc0_pwsize, void, env, tl)
-DEF_HELPER_2(mtc0_wired, void, env, tl)
-DEF_HELPER_2(mtc0_srsconf0, void, env, tl)
-DEF_HELPER_2(mtc0_srsconf1, void, env, tl)
-DEF_HELPER_2(mtc0_srsconf2, void, env, tl)
-DEF_HELPER_2(mtc0_srsconf3, void, env, tl)
-DEF_HELPER_2(mtc0_srsconf4, void, env, tl)
-DEF_HELPER_2(mtc0_hwrena, void, env, tl)
-DEF_HELPER_2(mtc0_pwctl, void, env, tl)
-DEF_HELPER_2(mtc0_count, void, env, tl)
-DEF_HELPER_2(mtc0_saari, void, env, tl)
-DEF_HELPER_2(mtc0_saar, void, env, tl)
-DEF_HELPER_2(mthc0_saar, void, env, tl)
-DEF_HELPER_2(mtc0_entryhi, void, env, tl)
-DEF_HELPER_2(mttc0_entryhi, void, env, tl)
-DEF_HELPER_2(mtc0_compare, void, env, tl)
-DEF_HELPER_2(mtc0_status, void, env, tl)
-DEF_HELPER_2(mttc0_status, void, env, tl)
-DEF_HELPER_2(mtc0_intctl, void, env, tl)
-DEF_HELPER_2(mtc0_srsctl, void, env, tl)
-DEF_HELPER_2(mtc0_cause, void, env, tl)
-DEF_HELPER_2(mttc0_cause, void, env, tl)
-DEF_HELPER_2(mtc0_ebase, void, env, tl)
-DEF_HELPER_2(mttc0_ebase, void, env, tl)
-DEF_HELPER_2(mtc0_config0, void, env, tl)
-DEF_HELPER_2(mtc0_config2, void, env, tl)
-DEF_HELPER_2(mtc0_config3, void, env, tl)
-DEF_HELPER_2(mtc0_config4, void, env, tl)
-DEF_HELPER_2(mtc0_config5, void, env, tl)
-DEF_HELPER_2(mtc0_lladdr, void, env, tl)
-DEF_HELPER_2(mtc0_maar, void, env, tl)
-DEF_HELPER_2(mthc0_maar, void, env, tl)
-DEF_HELPER_2(mtc0_maari, void, env, tl)
-DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32)
-DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32)
-DEF_HELPER_3(mthc0_watchhi, void, env, tl, i32)
-DEF_HELPER_2(mtc0_xcontext, void, env, tl)
-DEF_HELPER_2(mtc0_framemask, void, env, tl)
-DEF_HELPER_2(mtc0_debug, void, env, tl)
-DEF_HELPER_2(mttc0_debug, void, env, tl)
-DEF_HELPER_2(mtc0_performance0, void, env, tl)
-DEF_HELPER_2(mtc0_errctl, void, env, tl)
-DEF_HELPER_2(mtc0_taglo, void, env, tl)
-DEF_HELPER_2(mtc0_datalo, void, env, tl)
-DEF_HELPER_2(mtc0_taghi, void, env, tl)
-DEF_HELPER_2(mtc0_datahi, void, env, tl)
-
-#if defined(TARGET_MIPS64)
-DEF_HELPER_2(dmtc0_entrylo0, void, env, i64)
-DEF_HELPER_2(dmtc0_entrylo1, void, env, i64)
-#endif
-
-/* MIPS MT functions */
-DEF_HELPER_2(mftgpr, tl, env, i32)
-DEF_HELPER_2(mftlo, tl, env, i32)
-DEF_HELPER_2(mfthi, tl, env, i32)
-DEF_HELPER_2(mftacx, tl, env, i32)
-DEF_HELPER_1(mftdsp, tl, env)
-DEF_HELPER_3(mttgpr, void, env, tl, i32)
-DEF_HELPER_3(mttlo, void, env, tl, i32)
-DEF_HELPER_3(mtthi, void, env, tl, i32)
-DEF_HELPER_3(mttacx, void, env, tl, i32)
-DEF_HELPER_2(mttdsp, void, env, tl)
-DEF_HELPER_0(dmt, tl)
-DEF_HELPER_0(emt, tl)
-DEF_HELPER_1(dvpe, tl, env)
-DEF_HELPER_1(evpe, tl, env)
-
-/* R6 Multi-threading */
-DEF_HELPER_1(dvp, tl, env)
-DEF_HELPER_1(evp, tl, env)
-#endif /* !CONFIG_USER_ONLY */
-
/* microMIPS functions */
DEF_HELPER_4(lwm, void, env, tl, tl, i32)
DEF_HELPER_4(swm, void, env, tl, tl, i32)
@@ -364,21 +202,6 @@ FOP_PROTO(sune)
FOP_PROTO(sne)
#undef FOP_PROTO
-/* Special functions */
-#ifndef CONFIG_USER_ONLY
-DEF_HELPER_1(tlbwi, void, env)
-DEF_HELPER_1(tlbwr, void, env)
-DEF_HELPER_1(tlbp, void, env)
-DEF_HELPER_1(tlbr, void, env)
-DEF_HELPER_1(tlbinv, void, env)
-DEF_HELPER_1(tlbinvf, void, env)
-DEF_HELPER_1(di, tl, env)
-DEF_HELPER_1(ei, tl, env)
-DEF_HELPER_1(eret, void, env)
-DEF_HELPER_1(eretnc, void, env)
-DEF_HELPER_1(deret, void, env)
-DEF_HELPER_3(ginvt, void, env, tl, i32)
-#endif /* !CONFIG_USER_ONLY */
DEF_HELPER_1(rdhwr_cpunum, tl, env)
DEF_HELPER_1(rdhwr_synci_step, tl, env)
DEF_HELPER_1(rdhwr_cc, tl, env)
@@ -781,6 +604,8 @@ DEF_HELPER_FLAGS_3(dmthlip, 0, void, tl, tl, env)
DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env)
DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env)
-DEF_HELPER_3(cache, void, env, tl, i32)
+#ifndef CONFIG_USER_ONLY
+#include "tcg/sysemu_helper.h.inc"
+#endif /* !CONFIG_USER_ONLY */
-#include "msa_helper.h.inc"
+#include "tcg/msa_helper.h.inc"
diff --git a/target/mips/internal.h b/target/mips/internal.h
index 99264b8bf6..18d5da64a5 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -9,6 +9,9 @@
#define MIPS_INTERNAL_H
#include "exec/memattrs.h"
+#ifdef CONFIG_TCG
+#include "tcg/tcg-internal.h"
+#endif
/*
* MMU types, the first four entries have the same layout as the
@@ -71,21 +74,41 @@ struct mips_def_t {
int32_t SAARP;
};
+extern const char regnames[32][4];
+extern const char fregnames[32][4];
+
extern const struct mips_def_t mips_defs[];
extern const int mips_defs_number;
-void mips_cpu_do_interrupt(CPUState *cpu);
-bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req);
-void mips_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
-hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr);
+
+#define USEG_LIMIT ((target_ulong)(int32_t)0x7FFFFFFFUL)
+#define KSEG0_BASE ((target_ulong)(int32_t)0x80000000UL)
+#define KSEG1_BASE ((target_ulong)(int32_t)0xA0000000UL)
+#define KSEG2_BASE ((target_ulong)(int32_t)0xC0000000UL)
+#define KSEG3_BASE ((target_ulong)(int32_t)0xE0000000UL)
+
+#define KVM_KSEG0_BASE ((target_ulong)(int32_t)0x40000000UL)
+#define KVM_KSEG2_BASE ((target_ulong)(int32_t)0x60000000UL)
#if !defined(CONFIG_USER_ONLY)
+enum {
+ TLBRET_XI = -6,
+ TLBRET_RI = -5,
+ TLBRET_DIRTY = -4,
+ TLBRET_INVALID = -3,
+ TLBRET_NOMATCH = -2,
+ TLBRET_BADADDR = -1,
+ TLBRET_MATCH = 0
+};
+
+int get_physical_address(CPUMIPSState *env, hwaddr *physical,
+ int *prot, target_ulong real_address,
+ MMUAccessType access_type, int mmu_idx);
+hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+
typedef struct r4k_tlb_t r4k_tlb_t;
struct r4k_tlb_t {
target_ulong VPN;
@@ -125,35 +148,15 @@ struct CPUMIPSTLBContext {
} mmu;
};
-int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type);
-int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type);
-int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type);
-void r4k_helper_tlbwi(CPUMIPSState *env);
-void r4k_helper_tlbwr(CPUMIPSState *env);
-void r4k_helper_tlbp(CPUMIPSState *env);
-void r4k_helper_tlbr(CPUMIPSState *env);
-void r4k_helper_tlbinv(CPUMIPSState *env);
-void r4k_helper_tlbinvf(CPUMIPSState *env);
-void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra);
-uint32_t cpu_mips_get_random(CPUMIPSState *env);
-
-void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
- vaddr addr, unsigned size,
- MMUAccessType access_type,
- int mmu_idx, MemTxAttrs attrs,
- MemTxResult response, uintptr_t retaddr);
-hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address,
- MMUAccessType access_type);
-#endif
-
-#define cpu_signal_handler cpu_mips_signal_handler
+void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc);
+void cpu_mips_store_status(CPUMIPSState *env, target_ulong val);
+void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val);
-#ifndef CONFIG_USER_ONLY
extern const VMStateDescription vmstate_mips_cpu;
-#endif
+
+#endif /* !CONFIG_USER_ONLY */
+
+#define cpu_signal_handler cpu_mips_signal_handler
static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env)
{
@@ -197,8 +200,6 @@ static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
return r;
}
-void mips_tcg_init(void);
-
void msa_reset(CPUMIPSState *env);
/* cp0_timer.c */
@@ -208,14 +209,15 @@ void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value);
void cpu_mips_start_count(CPUMIPSState *env);
void cpu_mips_stop_count(CPUMIPSState *env);
-/* helper.c */
-void mmu_init(CPUMIPSState *env, const mips_def_t *def);
-bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
-
-/* op_helper.c */
-void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask);
+static inline void mips_env_set_pc(CPUMIPSState *env, target_ulong value)
+{
+ env->active_tc.PC = value & ~(target_ulong)1;
+ if (value & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ } else {
+ env->hflags &= ~(MIPS_HFLAG_M16);
+ }
+}
static inline void restore_pamask(CPUMIPSState *env)
{
@@ -397,21 +399,4 @@ static inline void compute_hflags(CPUMIPSState *env)
}
}
-void cpu_mips_tlb_flush(CPUMIPSState *env);
-void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc);
-void cpu_mips_store_status(CPUMIPSState *env, target_ulong val);
-void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val);
-
-const char *mips_exception_name(int32_t exception);
-
-void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, uint32_t exception,
- int error_code, uintptr_t pc);
-
-static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
- uint32_t exception,
- uintptr_t pc)
-{
- do_raise_exception_err(env, exception, 0, pc);
-}
-
#endif
diff --git a/target/mips/meson.build b/target/mips/meson.build
index 3b131c4a7f..2407a05d4c 100644
--- a/target/mips/meson.build
+++ b/target/mips/meson.build
@@ -1,52 +1,23 @@
-gen = [
- decodetree.process('mips32r6.decode', extra_args: '--static-decode=decode_mips32r6'),
- decodetree.process('mips64r6.decode', extra_args: '--static-decode=decode_mips64r6'),
- decodetree.process('msa32.decode', extra_args: '--static-decode=decode_msa32'),
- decodetree.process('msa64.decode', extra_args: '--static-decode=decode_msa64'),
- decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'),
-]
-
+mips_user_ss = ss.source_set()
+mips_softmmu_ss = ss.source_set()
mips_ss = ss.source_set()
mips_ss.add(files(
'cpu.c',
+ 'fpu.c',
'gdbstub.c',
-))
-mips_tcg_ss = ss.source_set()
-mips_tcg_ss.add(gen)
-mips_tcg_ss.add(files(
- 'dsp_helper.c',
- 'fpu_helper.c',
- 'lmmi_helper.c',
- 'msa_helper.c',
- 'msa_translate.c',
- 'op_helper.c',
- 'rel6_translate.c',
- 'tlb_helper.c',
- 'translate.c',
- 'translate_addr_const.c',
- 'txx9_translate.c',
-))
-mips_ss.add(when: ['CONFIG_TCG', 'TARGET_MIPS64'], if_true: files(
- 'tx79_translate.c',
-))
-mips_tcg_ss.add(when: 'TARGET_MIPS64', if_false: files(
- 'mxu_translate.c',
+ 'msa.c',
))
-mips_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'))
+if have_system
+ subdir('sysemu')
+endif
-mips_softmmu_ss = ss.source_set()
-mips_softmmu_ss.add(files(
- 'addr.c',
- 'cp0_timer.c',
- 'machine.c',
-))
-mips_softmmu_ss.add(when: 'CONFIG_TCG', if_true: files(
- 'cp0_helper.c',
- 'mips-semi.c',
-))
+if 'CONFIG_TCG' in config_all
+ subdir('tcg')
+endif
-mips_ss.add_all(when: 'CONFIG_TCG', if_true: [mips_tcg_ss])
+mips_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'))
target_arch += {'mips': mips_ss}
target_softmmu_arch += {'mips': mips_softmmu_ss}
+target_user_arch += {'mips': mips_user_ss}
diff --git a/target/mips/msa.c b/target/mips/msa.c
new file mode 100644
index 0000000000..61f1a9a593
--- /dev/null
+++ b/target/mips/msa.c
@@ -0,0 +1,60 @@
+/*
+ * MIPS SIMD Architecture Module Instruction emulation helpers for QEMU.
+ *
+ * Copyright (c) 2014 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "internal.h"
+#include "fpu/softfloat.h"
+#include "fpu_helper.h"
+
+void msa_reset(CPUMIPSState *env)
+{
+ if (!ase_msa_available(env)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ /* MSA access enabled */
+ env->CP0_Config5 |= 1 << CP0C5_MSAEn;
+ env->CP0_Status |= (1 << CP0St_CU1) | (1 << CP0St_FR);
+#endif
+
+ /*
+ * MSA CSR:
+ * - non-signaling floating point exception mode off (NX bit is 0)
+ * - Cause, Enables, and Flags are all 0
+ * - round to nearest / ties to even (RM bits are 0)
+ */
+ env->active_tc.msacsr = 0;
+
+ restore_msa_fp_status(env);
+
+ /* tininess detected after rounding.*/
+ set_float_detect_tininess(float_tininess_after_rounding,
+ &env->active_tc.msa_fp_status);
+
+ /* clear float_status exception flags */
+ set_float_exception_flags(0, &env->active_tc.msa_fp_status);
+
+ /* clear float_status nan mode */
+ set_default_nan_mode(0, &env->active_tc.msa_fp_status);
+
+ /* set proper signanling bit meaning ("1" means "quiet") */
+ set_snan_bit_is_one(0, &env->active_tc.msa_fp_status);
+}
diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c
deleted file mode 100644
index b80e8f7540..0000000000
--- a/target/mips/op_helper.c
+++ /dev/null
@@ -1,1210 +0,0 @@
-/*
- * MIPS emulation helpers for qemu.
- *
- * Copyright (c) 2004-2005 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "qemu/osdep.h"
-#include "cpu.h"
-#include "internal.h"
-#include "exec/helper-proto.h"
-#include "exec/exec-all.h"
-#include "exec/memop.h"
-#include "fpu_helper.h"
-
-/*****************************************************************************/
-/* Exceptions processing helpers */
-
-void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception,
- int error_code)
-{
- do_raise_exception_err(env, exception, error_code, 0);
-}
-
-void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
-{
- do_raise_exception(env, exception, GETPC());
-}
-
-void helper_raise_exception_debug(CPUMIPSState *env)
-{
- do_raise_exception(env, EXCP_DEBUG, 0);
-}
-
-static void raise_exception(CPUMIPSState *env, uint32_t exception)
-{
- do_raise_exception(env, exception, 0);
-}
-
-/* 64 bits arithmetic for 32 bits hosts */
-static inline uint64_t get_HILO(CPUMIPSState *env)
-{
- return ((uint64_t)(env->active_tc.HI[0]) << 32) |
- (uint32_t)env->active_tc.LO[0];
-}
-
-static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO)
-{
- env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
- return env->active_tc.HI[0] = (int32_t)(HILO >> 32);
-}
-
-static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO)
-{
- target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
- env->active_tc.HI[0] = (int32_t)(HILO >> 32);
- return tmp;
-}
-
-/* Multiplication variants of the vr54xx. */
-target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2));
-}
-
-target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 *
- (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, (uint64_t)get_HILO(env) +
- (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (uint64_t)get_HILO(env) +
- (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HI_LOT0(env, (uint64_t)get_HILO(env) -
- (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (uint64_t)get_HILO(env) -
- (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 *
- (uint64_t)(uint32_t)arg2);
-}
-
-target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 *
- (int64_t)(int32_t)arg2);
-}
-
-target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1,
- target_ulong arg2)
-{
- return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 *
- (uint64_t)(uint32_t)arg2);
-}
-
-static inline target_ulong bitswap(target_ulong v)
-{
- v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) |
- ((v & (target_ulong)0x5555555555555555ULL) << 1);
- v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) |
- ((v & (target_ulong)0x3333333333333333ULL) << 2);
- v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) |
- ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4);
- return v;
-}
-
-#ifdef TARGET_MIPS64
-target_ulong helper_dbitswap(target_ulong rt)
-{
- return bitswap(rt);
-}
-#endif
-
-target_ulong helper_bitswap(target_ulong rt)
-{
- return (int32_t)bitswap(rt);
-}
-
-target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx,
- uint32_t stripe)
-{
- int i;
- uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff);
- uint64_t tmp1 = tmp0;
- for (i = 0; i <= 46; i++) {
- int s;
- if (i & 0x8) {
- s = shift;
- } else {
- s = shiftx;
- }
-
- if (stripe != 0 && !(i & 0x4)) {
- s = ~s;
- }
- if (s & 0x10) {
- if (tmp0 & (1LL << (i + 16))) {
- tmp1 |= 1LL << i;
- } else {
- tmp1 &= ~(1LL << i);
- }
- }
- }
-
- uint64_t tmp2 = tmp1;
- for (i = 0; i <= 38; i++) {
- int s;
- if (i & 0x4) {
- s = shift;
- } else {
- s = shiftx;
- }
-
- if (s & 0x8) {
- if (tmp1 & (1LL << (i + 8))) {
- tmp2 |= 1LL << i;
- } else {
- tmp2 &= ~(1LL << i);
- }
- }
- }
-
- uint64_t tmp3 = tmp2;
- for (i = 0; i <= 34; i++) {
- int s;
- if (i & 0x2) {
- s = shift;
- } else {
- s = shiftx;
- }
- if (s & 0x4) {
- if (tmp2 & (1LL << (i + 4))) {
- tmp3 |= 1LL << i;
- } else {
- tmp3 &= ~(1LL << i);
- }
- }
- }
-
- uint64_t tmp4 = tmp3;
- for (i = 0; i <= 32; i++) {
- int s;
- if (i & 0x1) {
- s = shift;
- } else {
- s = shiftx;
- }
- if (s & 0x2) {
- if (tmp3 & (1LL << (i + 2))) {
- tmp4 |= 1LL << i;
- } else {
- tmp4 &= ~(1LL << i);
- }
- }
- }
-
- uint64_t tmp5 = tmp4;
- for (i = 0; i <= 31; i++) {
- int s;
- s = shift;
- if (s & 0x1) {
- if (tmp4 & (1LL << (i + 1))) {
- tmp5 |= 1LL << i;
- } else {
- tmp5 &= ~(1LL << i);
- }
- }
- }
-
- return (int64_t)(int32_t)(uint32_t)tmp5;
-}
-
-#ifndef CONFIG_USER_ONLY
-
-static inline hwaddr do_translate_address(CPUMIPSState *env,
- target_ulong address,
- MMUAccessType access_type,
- uintptr_t retaddr)
-{
- hwaddr paddr;
- CPUState *cs = env_cpu(env);
-
- paddr = cpu_mips_translate_address(env, address, access_type);
-
- if (paddr == -1LL) {
- cpu_loop_exit_restore(cs, retaddr);
- } else {
- return paddr;
- }
-}
-
-#define HELPER_LD_ATOMIC(name, insn, almask, do_cast) \
-target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \
-{ \
- if (arg & almask) { \
- if (!(env->hflags & MIPS_HFLAG_DM)) { \
- env->CP0_BadVAddr = arg; \
- } \
- do_raise_exception(env, EXCP_AdEL, GETPC()); \
- } \
- env->CP0_LLAddr = do_translate_address(env, arg, MMU_DATA_LOAD, GETPC()); \
- env->lladdr = arg; \
- env->llval = do_cast cpu_##insn##_mmuidx_ra(env, arg, mem_idx, GETPC()); \
- return env->llval; \
-}
-HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t))
-#ifdef TARGET_MIPS64
-HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong))
-#endif
-#undef HELPER_LD_ATOMIC
-#endif
-
-#ifdef TARGET_WORDS_BIGENDIAN
-#define GET_LMASK(v) ((v) & 3)
-#define GET_OFFSET(addr, offset) (addr + (offset))
-#else
-#define GET_LMASK(v) (((v) & 3) ^ 3)
-#define GET_OFFSET(addr, offset) (addr - (offset))
-#endif
-
-void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
- int mem_idx)
-{
- cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC());
-
- if (GET_LMASK(arg2) <= 2) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK(arg2) <= 1) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK(arg2) == 0) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)arg1,
- mem_idx, GETPC());
- }
-}
-
-void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
- int mem_idx)
-{
- cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
-
- if (GET_LMASK(arg2) >= 1) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK(arg2) >= 2) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK(arg2) == 3) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24),
- mem_idx, GETPC());
- }
-}
-
-#if defined(TARGET_MIPS64)
-/*
- * "half" load and stores. We must do the memory access inline,
- * or fault handling won't work.
- */
-#ifdef TARGET_WORDS_BIGENDIAN
-#define GET_LMASK64(v) ((v) & 7)
-#else
-#define GET_LMASK64(v) (((v) & 7) ^ 7)
-#endif
-
-void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
- int mem_idx)
-{
- cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC());
-
- if (GET_LMASK64(arg2) <= 6) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 5) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 4) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 3) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 2) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 1) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) <= 0) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 7), (uint8_t)arg1,
- mem_idx, GETPC());
- }
-}
-
-void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
- int mem_idx)
-{
- cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
-
- if (GET_LMASK64(arg2) >= 1) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) >= 2) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) >= 3) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) >= 4) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) >= 5) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) >= 6) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48),
- mem_idx, GETPC());
- }
-
- if (GET_LMASK64(arg2) == 7) {
- cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56),
- mem_idx, GETPC());
- }
-}
-#endif /* TARGET_MIPS64 */
-
-static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 };
-
-void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
- uint32_t mem_idx)
-{
- target_ulong base_reglist = reglist & 0xf;
- target_ulong do_r31 = reglist & 0x10;
-
- if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
- target_ulong i;
-
- for (i = 0; i < base_reglist; i++) {
- env->active_tc.gpr[multiple_regs[i]] =
- (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
- addr += 4;
- }
- }
-
- if (do_r31) {
- env->active_tc.gpr[31] =
- (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
- }
-}
-
-void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
- uint32_t mem_idx)
-{
- target_ulong base_reglist = reglist & 0xf;
- target_ulong do_r31 = reglist & 0x10;
-
- if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
- target_ulong i;
-
- for (i = 0; i < base_reglist; i++) {
- cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
- mem_idx, GETPC());
- addr += 4;
- }
- }
-
- if (do_r31) {
- cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
- }
-}
-
-#if defined(TARGET_MIPS64)
-void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
- uint32_t mem_idx)
-{
- target_ulong base_reglist = reglist & 0xf;
- target_ulong do_r31 = reglist & 0x10;
-
- if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
- target_ulong i;
-
- for (i = 0; i < base_reglist; i++) {
- env->active_tc.gpr[multiple_regs[i]] =
- cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
- addr += 8;
- }
- }
-
- if (do_r31) {
- env->active_tc.gpr[31] =
- cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
- }
-}
-
-void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
- uint32_t mem_idx)
-{
- target_ulong base_reglist = reglist & 0xf;
- target_ulong do_r31 = reglist & 0x10;
-
- if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
- target_ulong i;
-
- for (i = 0; i < base_reglist; i++) {
- cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
- mem_idx, GETPC());
- addr += 8;
- }
- }
-
- if (do_r31) {
- cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
- }
-}
-#endif
-
-
-void helper_fork(target_ulong arg1, target_ulong arg2)
-{
- /*
- * arg1 = rt, arg2 = rs
- * TODO: store to TC register
- */
-}
-
-target_ulong helper_yield(CPUMIPSState *env, target_ulong arg)
-{
- target_long arg1 = arg;
-
- if (arg1 < 0) {
- /* No scheduling policy implemented. */
- if (arg1 != -2) {
- if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) &&
- env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) {
- env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
- env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT;
- do_raise_exception(env, EXCP_THREAD, GETPC());
- }
- }
- } else if (arg1 == 0) {
- if (0) {
- /* TODO: TC underflow */
- env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
- do_raise_exception(env, EXCP_THREAD, GETPC());
- } else {
- /* TODO: Deallocate TC */
- }
- } else if (arg1 > 0) {
- /* Yield qualifier inputs not implemented. */
- env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
- env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT;
- do_raise_exception(env, EXCP_THREAD, GETPC());
- }
- return env->CP0_YQMask;
-}
-
-#ifndef CONFIG_USER_ONLY
-/* TLB management */
-static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first)
-{
- /* Discard entries from env->tlb[first] onwards. */
- while (env->tlb->tlb_in_use > first) {
- r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0);
- }
-}
-
-static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo)
-{
-#if defined(TARGET_MIPS64)
- return extract64(entrylo, 6, 54);
-#else
- return extract64(entrylo, 6, 24) | /* PFN */
- (extract64(entrylo, 32, 32) << 24); /* PFNX */
-#endif
-}
-
-static void r4k_fill_tlb(CPUMIPSState *env, int idx)
-{
- r4k_tlb_t *tlb;
- uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1);
-
- /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
- tlb = &env->tlb->mmu.r4k.tlb[idx];
- if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) {
- tlb->EHINV = 1;
- return;
- }
- tlb->EHINV = 0;
- tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
-#if defined(TARGET_MIPS64)
- tlb->VPN &= env->SEGMask;
-#endif
- tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
- tlb->MMID = env->CP0_MemoryMapID;
- tlb->PageMask = env->CP0_PageMask;
- tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
- tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
- tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
- tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
- tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1;
- tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1;
- tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12;
- tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
- tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
- tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
- tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1;
- tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1;
- tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12;
-}
-
-void r4k_helper_tlbinv(CPUMIPSState *env)
-{
- bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
- uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
- uint32_t MMID = env->CP0_MemoryMapID;
- uint32_t tlb_mmid;
- r4k_tlb_t *tlb;
- int idx;
-
- MMID = mi ? MMID : (uint32_t) ASID;
- for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
- tlb = &env->tlb->mmu.r4k.tlb[idx];
- tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
- if (!tlb->G && tlb_mmid == MMID) {
- tlb->EHINV = 1;
- }
- }
- cpu_mips_tlb_flush(env);
-}
-
-void r4k_helper_tlbinvf(CPUMIPSState *env)
-{
- int idx;
-
- for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
- env->tlb->mmu.r4k.tlb[idx].EHINV = 1;
- }
- cpu_mips_tlb_flush(env);
-}
-
-void r4k_helper_tlbwi(CPUMIPSState *env)
-{
- bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
- target_ulong VPN;
- uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
- uint32_t MMID = env->CP0_MemoryMapID;
- uint32_t tlb_mmid;
- bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1;
- r4k_tlb_t *tlb;
- int idx;
-
- MMID = mi ? MMID : (uint32_t) ASID;
-
- idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
- tlb = &env->tlb->mmu.r4k.tlb[idx];
- VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
-#if defined(TARGET_MIPS64)
- VPN &= env->SEGMask;
-#endif
- EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0;
- G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
- V0 = (env->CP0_EntryLo0 & 2) != 0;
- D0 = (env->CP0_EntryLo0 & 4) != 0;
- XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1;
- RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1;
- V1 = (env->CP0_EntryLo1 & 2) != 0;
- D1 = (env->CP0_EntryLo1 & 4) != 0;
- XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1;
- RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1;
-
- tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
- /*
- * Discard cached TLB entries, unless tlbwi is just upgrading access
- * permissions on the current entry.
- */
- if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G ||
- (!tlb->EHINV && EHINV) ||
- (tlb->V0 && !V0) || (tlb->D0 && !D0) ||
- (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) ||
- (tlb->V1 && !V1) || (tlb->D1 && !D1) ||
- (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) {
- r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
- }
-
- r4k_invalidate_tlb(env, idx, 0);
- r4k_fill_tlb(env, idx);
-}
-
-void r4k_helper_tlbwr(CPUMIPSState *env)
-{
- int r = cpu_mips_get_random(env);
-
- r4k_invalidate_tlb(env, r, 1);
- r4k_fill_tlb(env, r);
-}
-
-void r4k_helper_tlbp(CPUMIPSState *env)
-{
- bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
- r4k_tlb_t *tlb;
- target_ulong mask;
- target_ulong tag;
- target_ulong VPN;
- uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
- uint32_t MMID = env->CP0_MemoryMapID;
- uint32_t tlb_mmid;
- int i;
-
- MMID = mi ? MMID : (uint32_t) ASID;
- for (i = 0; i < env->tlb->nb_tlb; i++) {
- tlb = &env->tlb->mmu.r4k.tlb[i];
- /* 1k pages are not supported. */
- mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
- tag = env->CP0_EntryHi & ~mask;
- VPN = tlb->VPN & ~mask;
-#if defined(TARGET_MIPS64)
- tag &= env->SEGMask;
-#endif
- tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
- /* Check ASID/MMID, virtual page number & size */
- if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) {
- /* TLB match */
- env->CP0_Index = i;
- break;
- }
- }
- if (i == env->tlb->nb_tlb) {
- /* No match. Discard any shadow entries, if any of them match. */
- for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) {
- tlb = &env->tlb->mmu.r4k.tlb[i];
- /* 1k pages are not supported. */
- mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
- tag = env->CP0_EntryHi & ~mask;
- VPN = tlb->VPN & ~mask;
-#if defined(TARGET_MIPS64)
- tag &= env->SEGMask;
-#endif
- tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
- /* Check ASID/MMID, virtual page number & size */
- if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) {
- r4k_mips_tlb_flush_extra(env, i);
- break;
- }
- }
-
- env->CP0_Index |= 0x80000000;
- }
-}
-
-static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn)
-{
-#if defined(TARGET_MIPS64)
- return tlb_pfn << 6;
-#else
- return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */
- (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */
-#endif
-}
-
-void r4k_helper_tlbr(CPUMIPSState *env)
-{
- bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
- uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
- uint32_t MMID = env->CP0_MemoryMapID;
- uint32_t tlb_mmid;
- r4k_tlb_t *tlb;
- int idx;
-
- MMID = mi ? MMID : (uint32_t) ASID;
- idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
- tlb = &env->tlb->mmu.r4k.tlb[idx];
-
- tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
- /* If this will change the current ASID/MMID, flush qemu's TLB. */
- if (MMID != tlb_mmid) {
- cpu_mips_tlb_flush(env);
- }
-
- r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
-
- if (tlb->EHINV) {
- env->CP0_EntryHi = 1 << CP0EnHi_EHINV;
- env->CP0_PageMask = 0;
- env->CP0_EntryLo0 = 0;
- env->CP0_EntryLo1 = 0;
- } else {
- env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID;
- env->CP0_MemoryMapID = tlb->MMID;
- env->CP0_PageMask = tlb->PageMask;
- env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
- ((uint64_t)tlb->RI0 << CP0EnLo_RI) |
- ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) |
- get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12);
- env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
- ((uint64_t)tlb->RI1 << CP0EnLo_RI) |
- ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) |
- get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12);
- }
-}
-
-void helper_tlbwi(CPUMIPSState *env)
-{
- env->tlb->helper_tlbwi(env);
-}
-
-void helper_tlbwr(CPUMIPSState *env)
-{
- env->tlb->helper_tlbwr(env);
-}
-
-void helper_tlbp(CPUMIPSState *env)
-{
- env->tlb->helper_tlbp(env);
-}
-
-void helper_tlbr(CPUMIPSState *env)
-{
- env->tlb->helper_tlbr(env);
-}
-
-void helper_tlbinv(CPUMIPSState *env)
-{
- env->tlb->helper_tlbinv(env);
-}
-
-void helper_tlbinvf(CPUMIPSState *env)
-{
- env->tlb->helper_tlbinvf(env);
-}
-
-static void global_invalidate_tlb(CPUMIPSState *env,
- uint32_t invMsgVPN2,
- uint8_t invMsgR,
- uint32_t invMsgMMid,
- bool invAll,
- bool invVAMMid,
- bool invMMid,
- bool invVA)
-{
-
- int idx;
- r4k_tlb_t *tlb;
- bool VAMatch;
- bool MMidMatch;
-
- for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
- tlb = &env->tlb->mmu.r4k.tlb[idx];
- VAMatch =
- (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask))
-#ifdef TARGET_MIPS64
- &&
- (extract64(env->CP0_EntryHi, 62, 2) == invMsgR)
-#endif
- );
- MMidMatch = tlb->MMID == invMsgMMid;
- if ((invAll && (idx > env->CP0_Wired)) ||
- (VAMatch && invVAMMid && (tlb->G || MMidMatch)) ||
- (VAMatch && invVA) ||
- (MMidMatch && !(tlb->G) && invMMid)) {
- tlb->EHINV = 1;
- }
- }
- cpu_mips_tlb_flush(env);
-}
-
-void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type)
-{
- bool invAll = type == 0;
- bool invVA = type == 1;
- bool invMMid = type == 2;
- bool invVAMMid = type == 3;
- uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1);
- uint8_t invMsgR = 0;
- uint32_t invMsgMMid = env->CP0_MemoryMapID;
- CPUState *other_cs = first_cpu;
-
-#ifdef TARGET_MIPS64
- invMsgR = extract64(arg, 62, 2);
-#endif
-
- CPU_FOREACH(other_cs) {
- MIPSCPU *other_cpu = MIPS_CPU(other_cs);
- global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid,
- invAll, invVAMMid, invMMid, invVA);
- }
-}
-
-/* Specials */
-target_ulong helper_di(CPUMIPSState *env)
-{
- target_ulong t0 = env->CP0_Status;
-
- env->CP0_Status = t0 & ~(1 << CP0St_IE);
- return t0;
-}
-
-target_ulong helper_ei(CPUMIPSState *env)
-{
- target_ulong t0 = env->CP0_Status;
-
- env->CP0_Status = t0 | (1 << CP0St_IE);
- return t0;
-}
-
-static void debug_pre_eret(CPUMIPSState *env)
-{
- if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
- qemu_log("ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
- env->active_tc.PC, env->CP0_EPC);
- if (env->CP0_Status & (1 << CP0St_ERL)) {
- qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
- }
- if (env->hflags & MIPS_HFLAG_DM) {
- qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
- }
- qemu_log("\n");
- }
-}
-
-static void debug_post_eret(CPUMIPSState *env)
-{
- if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
- qemu_log(" => PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
- env->active_tc.PC, env->CP0_EPC);
- if (env->CP0_Status & (1 << CP0St_ERL)) {
- qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
- }
- if (env->hflags & MIPS_HFLAG_DM) {
- qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
- }
- switch (cpu_mmu_index(env, false)) {
- case 3:
- qemu_log(", ERL\n");
- break;
- case MIPS_HFLAG_UM:
- qemu_log(", UM\n");
- break;
- case MIPS_HFLAG_SM:
- qemu_log(", SM\n");
- break;
- case MIPS_HFLAG_KM:
- qemu_log("\n");
- break;
- default:
- cpu_abort(env_cpu(env), "Invalid MMU mode!\n");
- break;
- }
- }
-}
-
-static void set_pc(CPUMIPSState *env, target_ulong error_pc)
-{
- env->active_tc.PC = error_pc & ~(target_ulong)1;
- if (error_pc & 1) {
- env->hflags |= MIPS_HFLAG_M16;
- } else {
- env->hflags &= ~(MIPS_HFLAG_M16);
- }
-}
-
-static inline void exception_return(CPUMIPSState *env)
-{
- debug_pre_eret(env);
- if (env->CP0_Status & (1 << CP0St_ERL)) {
- set_pc(env, env->CP0_ErrorEPC);
- env->CP0_Status &= ~(1 << CP0St_ERL);
- } else {
- set_pc(env, env->CP0_EPC);
- env->CP0_Status &= ~(1 << CP0St_EXL);
- }
- compute_hflags(env);
- debug_post_eret(env);
-}
-
-void helper_eret(CPUMIPSState *env)
-{
- exception_return(env);
- env->CP0_LLAddr = 1;
- env->lladdr = 1;
-}
-
-void helper_eretnc(CPUMIPSState *env)
-{
- exception_return(env);
-}
-
-void helper_deret(CPUMIPSState *env)
-{
- debug_pre_eret(env);
-
- env->hflags &= ~MIPS_HFLAG_DM;
- compute_hflags(env);
-
- set_pc(env, env->CP0_DEPC);
-
- debug_post_eret(env);
-}
-#endif /* !CONFIG_USER_ONLY */
-
-static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc)
-{
- if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) {
- return;
- }
- do_raise_exception(env, EXCP_RI, pc);
-}
-
-target_ulong helper_rdhwr_cpunum(CPUMIPSState *env)
-{
- check_hwrena(env, 0, GETPC());
- return env->CP0_EBase & 0x3ff;
-}
-
-target_ulong helper_rdhwr_synci_step(CPUMIPSState *env)
-{
- check_hwrena(env, 1, GETPC());
- return env->SYNCI_Step;
-}
-
-target_ulong helper_rdhwr_cc(CPUMIPSState *env)
-{
- check_hwrena(env, 2, GETPC());
-#ifdef CONFIG_USER_ONLY
- return env->CP0_Count;
-#else
- return (int32_t)cpu_mips_get_count(env);
-#endif
-}
-
-target_ulong helper_rdhwr_ccres(CPUMIPSState *env)
-{
- check_hwrena(env, 3, GETPC());
- return env->CCRes;
-}
-
-target_ulong helper_rdhwr_performance(CPUMIPSState *env)
-{
- check_hwrena(env, 4, GETPC());
- return env->CP0_Performance0;
-}
-
-target_ulong helper_rdhwr_xnp(CPUMIPSState *env)
-{
- check_hwrena(env, 5, GETPC());
- return (env->CP0_Config5 >> CP0C5_XNP) & 1;
-}
-
-void helper_pmon(CPUMIPSState *env, int function)
-{
- function /= 2;
- switch (function) {
- case 2: /* TODO: char inbyte(int waitflag); */
- if (env->active_tc.gpr[4] == 0) {
- env->active_tc.gpr[2] = -1;
- }
- /* Fall through */
- case 11: /* TODO: char inbyte (void); */
- env->active_tc.gpr[2] = -1;
- break;
- case 3:
- case 12:
- printf("%c", (char)(env->active_tc.gpr[4] & 0xFF));
- break;
- case 17:
- break;
- case 158:
- {
- unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4];
- printf("%s", fmt);
- }
- break;
- }
-}
-
-void helper_wait(CPUMIPSState *env)
-{
- CPUState *cs = env_cpu(env);
-
- cs->halted = 1;
- cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
- /*
- * Last instruction in the block, PC was updated before
- * - no need to recover PC and icount.
- */
- raise_exception(env, EXCP_HLT);
-}
-
-#if !defined(CONFIG_USER_ONLY)
-
-void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
- int error_code = 0;
- int excp;
-
- if (!(env->hflags & MIPS_HFLAG_DM)) {
- env->CP0_BadVAddr = addr;
- }
-
- if (access_type == MMU_DATA_STORE) {
- excp = EXCP_AdES;
- } else {
- excp = EXCP_AdEL;
- if (access_type == MMU_INST_FETCH) {
- error_code |= EXCP_INST_NOTAVAIL;
- }
- }
-
- do_raise_exception_err(env, excp, error_code, retaddr);
-}
-
-void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
- vaddr addr, unsigned size,
- MMUAccessType access_type,
- int mmu_idx, MemTxAttrs attrs,
- MemTxResult response, uintptr_t retaddr)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
-
- if (access_type == MMU_INST_FETCH) {
- do_raise_exception(env, EXCP_IBE, retaddr);
- } else {
- do_raise_exception(env, EXCP_DBE, retaddr);
- }
-}
-#endif /* !CONFIG_USER_ONLY */
-
-void helper_cache(CPUMIPSState *env, target_ulong addr, uint32_t op)
-{
-#ifndef CONFIG_USER_ONLY
- static const char *const type_name[] = {
- "Primary Instruction",
- "Primary Data or Unified Primary",
- "Tertiary",
- "Secondary"
- };
- uint32_t cache_type = extract32(op, 0, 2);
- uint32_t cache_operation = extract32(op, 2, 3);
- target_ulong index = addr & 0x1fffffff;
-
- switch (cache_operation) {
- case 0b010: /* Index Store Tag */
- memory_region_dispatch_write(env->itc_tag, index, env->CP0_TagLo,
- MO_64, MEMTXATTRS_UNSPECIFIED);
- break;
- case 0b001: /* Index Load Tag */
- memory_region_dispatch_read(env->itc_tag, index, &env->CP0_TagLo,
- MO_64, MEMTXATTRS_UNSPECIFIED);
- break;
- case 0b000: /* Index Invalidate */
- case 0b100: /* Hit Invalidate */
- case 0b110: /* Hit Writeback */
- /* no-op */
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "cache operation:%u (type: %s cache)\n",
- cache_operation, type_name[cache_type]);
- break;
- }
-#endif
-}
diff --git a/target/mips/addr.c b/target/mips/sysemu/addr.c
index 86f1c129c9..86f1c129c9 100644
--- a/target/mips/addr.c
+++ b/target/mips/sysemu/addr.c
diff --git a/target/mips/sysemu/cp0.c b/target/mips/sysemu/cp0.c
new file mode 100644
index 0000000000..bae37f515b
--- /dev/null
+++ b/target/mips/sysemu/cp0.c
@@ -0,0 +1,123 @@
+/*
+ * QEMU MIPS CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "internal.h"
+#include "exec/exec-all.h"
+
+/* Called for updates to CP0_Status. */
+void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
+{
+ int32_t tcstatus, *tcst;
+ uint32_t v = cpu->CP0_Status;
+ uint32_t cu, mx, asid, ksu;
+ uint32_t mask = ((1 << CP0TCSt_TCU3)
+ | (1 << CP0TCSt_TCU2)
+ | (1 << CP0TCSt_TCU1)
+ | (1 << CP0TCSt_TCU0)
+ | (1 << CP0TCSt_TMX)
+ | (3 << CP0TCSt_TKSU)
+ | (0xff << CP0TCSt_TASID));
+
+ cu = (v >> CP0St_CU0) & 0xf;
+ mx = (v >> CP0St_MX) & 0x1;
+ ksu = (v >> CP0St_KSU) & 0x3;
+ asid = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+
+ tcstatus = cu << CP0TCSt_TCU0;
+ tcstatus |= mx << CP0TCSt_TMX;
+ tcstatus |= ksu << CP0TCSt_TKSU;
+ tcstatus |= asid;
+
+ if (tc == cpu->current_tc) {
+ tcst = &cpu->active_tc.CP0_TCStatus;
+ } else {
+ tcst = &cpu->tcs[tc].CP0_TCStatus;
+ }
+
+ *tcst &= ~mask;
+ *tcst |= tcstatus;
+ compute_hflags(cpu);
+}
+
+void cpu_mips_store_status(CPUMIPSState *env, target_ulong val)
+{
+ uint32_t mask = env->CP0_Status_rw_bitmask;
+ target_ulong old = env->CP0_Status;
+
+ if (env->insn_flags & ISA_MIPS_R6) {
+ bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3;
+#if defined(TARGET_MIPS64)
+ uint32_t ksux = (1 << CP0St_KX) & val;
+ ksux |= (ksux >> 1) & val; /* KX = 0 forces SX to be 0 */
+ ksux |= (ksux >> 1) & val; /* SX = 0 forces UX to be 0 */
+ val = (val & ~(7 << CP0St_UX)) | ksux;
+#endif
+ if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) {
+ mask &= ~(3 << CP0St_KSU);
+ }
+ mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val);
+ }
+
+ env->CP0_Status = (old & ~mask) | (val & mask);
+#if defined(TARGET_MIPS64)
+ if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) {
+ /* Access to at least one of the 64-bit segments has been disabled */
+ tlb_flush(env_cpu(env));
+ }
+#endif
+ if (ase_mt_available(env)) {
+ sync_c0_status(env, env, env->current_tc);
+ } else {
+ compute_hflags(env);
+ }
+}
+
+void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val)
+{
+ uint32_t mask = 0x00C00300;
+ uint32_t old = env->CP0_Cause;
+ int i;
+
+ if (env->insn_flags & ISA_MIPS_R2) {
+ mask |= 1 << CP0Ca_DC;
+ }
+ if (env->insn_flags & ISA_MIPS_R6) {
+ mask &= ~((1 << CP0Ca_WP) & val);
+ }
+
+ env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask);
+
+ if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) {
+ if (env->CP0_Cause & (1 << CP0Ca_DC)) {
+ cpu_mips_stop_count(env);
+ } else {
+ cpu_mips_start_count(env);
+ }
+ }
+
+ /* Set/reset software interrupts */
+ for (i = 0 ; i < 2 ; i++) {
+ if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
+ cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+ }
+ }
+}
diff --git a/target/mips/cp0_timer.c b/target/mips/sysemu/cp0_timer.c
index 70de95d338..70de95d338 100644
--- a/target/mips/cp0_timer.c
+++ b/target/mips/sysemu/cp0_timer.c
diff --git a/target/mips/machine.c b/target/mips/sysemu/machine.c
index b5fda6a278..80d37f9c2f 100644
--- a/target/mips/machine.c
+++ b/target/mips/sysemu/machine.c
@@ -81,6 +81,9 @@ const VMStateDescription vmstate_inactive_fpu = {
static VMStateField vmstate_tc_fields[] = {
VMSTATE_UINTTL_ARRAY(gpr, TCState, 32),
+#if defined(TARGET_MIPS64)
+ VMSTATE_UINT64_ARRAY(gpr_hi, TCState, 32),
+#endif /* TARGET_MIPS64 */
VMSTATE_UINTTL(PC, TCState),
VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC),
VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC),
@@ -95,20 +98,22 @@ static VMStateField vmstate_tc_fields[] = {
VMSTATE_INT32(CP0_Debug_tcstatus, TCState),
VMSTATE_UINTTL(CP0_UserLocal, TCState),
VMSTATE_INT32(msacsr, TCState),
+ VMSTATE_UINTTL_ARRAY(mxu_gpr, TCState, NUMBER_OF_MXU_REGISTERS - 1),
+ VMSTATE_UINTTL(mxu_cr, TCState),
VMSTATE_END_OF_LIST()
};
const VMStateDescription vmstate_tc = {
.name = "cpu/tc",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = vmstate_tc_fields
};
const VMStateDescription vmstate_inactive_tc = {
.name = "cpu/inactive_tc",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = vmstate_tc_fields
};
@@ -213,8 +218,8 @@ const VMStateDescription vmstate_tlb = {
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
- .version_id = 20,
- .minimum_version_id = 20,
+ .version_id = 21,
+ .minimum_version_id = 21,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
/* Active TC */
@@ -241,6 +246,7 @@ const VMStateDescription vmstate_mips_cpu = {
/* Remaining CP0 registers */
VMSTATE_INT32(env.CP0_Index, MIPSCPU),
+ VMSTATE_INT32(env.CP0_VPControl, MIPSCPU),
VMSTATE_INT32(env.CP0_Random, MIPSCPU),
VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU),
VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU),
@@ -251,6 +257,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU),
VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU),
VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU),
+ VMSTATE_INT32(env.CP0_GlobalNumber, MIPSCPU),
VMSTATE_UINTTL(env.CP0_Context, MIPSCPU),
VMSTATE_INT32(env.CP0_MemoryMapID, MIPSCPU),
VMSTATE_INT32(env.CP0_PageMask, MIPSCPU),
@@ -286,6 +293,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU),
VMSTATE_INT32(env.CP0_PRid, MIPSCPU),
VMSTATE_UINTTL(env.CP0_EBase, MIPSCPU),
+ VMSTATE_UINTTL(env.CP0_CMGCRBase, MIPSCPU),
VMSTATE_INT32(env.CP0_Config0, MIPSCPU),
VMSTATE_INT32(env.CP0_Config1, MIPSCPU),
VMSTATE_INT32(env.CP0_Config2, MIPSCPU),
@@ -305,6 +313,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_Debug, MIPSCPU),
VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU),
VMSTATE_INT32(env.CP0_Performance0, MIPSCPU),
+ VMSTATE_INT32(env.CP0_ErrCtl, MIPSCPU),
VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU),
VMSTATE_INT32(env.CP0_DataLo, MIPSCPU),
VMSTATE_INT32(env.CP0_TagHi, MIPSCPU),
diff --git a/target/mips/sysemu/meson.build b/target/mips/sysemu/meson.build
new file mode 100644
index 0000000000..cefc227582
--- /dev/null
+++ b/target/mips/sysemu/meson.build
@@ -0,0 +1,7 @@
+mips_softmmu_ss.add(files(
+ 'addr.c',
+ 'cp0.c',
+ 'cp0_timer.c',
+ 'machine.c',
+ 'physaddr.c',
+))
diff --git a/target/mips/sysemu/physaddr.c b/target/mips/sysemu/physaddr.c
new file mode 100644
index 0000000000..1918633aa1
--- /dev/null
+++ b/target/mips/sysemu/physaddr.c
@@ -0,0 +1,257 @@
+/*
+ * MIPS TLB (Translation lookaside buffer) helpers.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "../internal.h"
+
+static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx)
+{
+ /*
+ * Interpret access control mode and mmu_idx.
+ * AdE? TLB?
+ * AM K S U E K S U E
+ * UK 0 0 1 1 0 0 - - 0
+ * MK 1 0 1 1 0 1 - - !eu
+ * MSK 2 0 0 1 0 1 1 - !eu
+ * MUSK 3 0 0 0 0 1 1 1 !eu
+ * MUSUK 4 0 0 0 0 0 1 1 0
+ * USK 5 0 0 1 0 0 0 - 0
+ * - 6 - - - - - - - -
+ * UUSK 7 0 0 0 0 0 0 0 0
+ */
+ int32_t adetlb_mask;
+
+ switch (mmu_idx) {
+ case 3: /* ERL */
+ /* If EU is set, always unmapped */
+ if (eu) {
+ return 0;
+ }
+ /* fall through */
+ case MIPS_HFLAG_KM:
+ /* Never AdE, TLB mapped if AM={1,2,3} */
+ adetlb_mask = 0x70000000;
+ goto check_tlb;
+
+ case MIPS_HFLAG_SM:
+ /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */
+ adetlb_mask = 0xc0380000;
+ goto check_ade;
+
+ case MIPS_HFLAG_UM:
+ /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */
+ adetlb_mask = 0xe4180000;
+ /* fall through */
+ check_ade:
+ /* does this AM cause AdE in current execution mode */
+ if ((adetlb_mask << am) < 0) {
+ return TLBRET_BADADDR;
+ }
+ adetlb_mask <<= 8;
+ /* fall through */
+ check_tlb:
+ /* is this AM mapped in current execution mode */
+ return ((adetlb_mask << am) < 0);
+ default:
+ assert(0);
+ return TLBRET_BADADDR;
+ };
+}
+
+static int get_seg_physical_address(CPUMIPSState *env, hwaddr *physical,
+ int *prot, target_ulong real_address,
+ MMUAccessType access_type, int mmu_idx,
+ unsigned int am, bool eu,
+ target_ulong segmask,
+ hwaddr physical_base)
+{
+ int mapped = is_seg_am_mapped(am, eu, mmu_idx);
+
+ if (mapped < 0) {
+ /* is_seg_am_mapped can report TLBRET_BADADDR */
+ return mapped;
+ } else if (mapped) {
+ /* The segment is TLB mapped */
+ return env->tlb->map_address(env, physical, prot, real_address,
+ access_type);
+ } else {
+ /* The segment is unmapped */
+ *physical = physical_base | (real_address & segmask);
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return TLBRET_MATCH;
+ }
+}
+
+static int get_segctl_physical_address(CPUMIPSState *env, hwaddr *physical,
+ int *prot, target_ulong real_address,
+ MMUAccessType access_type, int mmu_idx,
+ uint16_t segctl, target_ulong segmask)
+{
+ unsigned int am = (segctl & CP0SC_AM_MASK) >> CP0SC_AM;
+ bool eu = (segctl >> CP0SC_EU) & 1;
+ hwaddr pa = ((hwaddr)segctl & CP0SC_PA_MASK) << 20;
+
+ return get_seg_physical_address(env, physical, prot, real_address,
+ access_type, mmu_idx, am, eu, segmask,
+ pa & ~(hwaddr)segmask);
+}
+
+int get_physical_address(CPUMIPSState *env, hwaddr *physical,
+ int *prot, target_ulong real_address,
+ MMUAccessType access_type, int mmu_idx)
+{
+ /* User mode can only access useg/xuseg */
+#if defined(TARGET_MIPS64)
+ int user_mode = mmu_idx == MIPS_HFLAG_UM;
+ int supervisor_mode = mmu_idx == MIPS_HFLAG_SM;
+ int kernel_mode = !user_mode && !supervisor_mode;
+ int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
+ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
+ int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
+#endif
+ int ret = TLBRET_MATCH;
+ /* effective address (modified for KVM T&E kernel segments) */
+ target_ulong address = real_address;
+
+ if (mips_um_ksegs_enabled()) {
+ /* KVM T&E adds guest kernel segments in useg */
+ if (real_address >= KVM_KSEG0_BASE) {
+ if (real_address < KVM_KSEG2_BASE) {
+ /* kseg0 */
+ address += KSEG0_BASE - KVM_KSEG0_BASE;
+ } else if (real_address <= USEG_LIMIT) {
+ /* kseg2/3 */
+ address += KSEG2_BASE - KVM_KSEG2_BASE;
+ }
+ }
+ }
+
+ if (address <= USEG_LIMIT) {
+ /* useg */
+ uint16_t segctl;
+
+ if (address >= 0x40000000UL) {
+ segctl = env->CP0_SegCtl2;
+ } else {
+ segctl = env->CP0_SegCtl2 >> 16;
+ }
+ ret = get_segctl_physical_address(env, physical, prot,
+ real_address, access_type,
+ mmu_idx, segctl, 0x3FFFFFFF);
+#if defined(TARGET_MIPS64)
+ } else if (address < 0x4000000000000000ULL) {
+ /* xuseg */
+ if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot,
+ real_address, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0x8000000000000000ULL) {
+ /* xsseg */
+ if ((supervisor_mode || kernel_mode) &&
+ SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot,
+ real_address, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0xC000000000000000ULL) {
+ /* xkphys */
+ if ((address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
+ /* KX/SX/UX bit to check for each xkphys EVA access mode */
+ static const uint8_t am_ksux[8] = {
+ [CP0SC_AM_UK] = (1u << CP0St_KX),
+ [CP0SC_AM_MK] = (1u << CP0St_KX),
+ [CP0SC_AM_MSK] = (1u << CP0St_SX),
+ [CP0SC_AM_MUSK] = (1u << CP0St_UX),
+ [CP0SC_AM_MUSUK] = (1u << CP0St_UX),
+ [CP0SC_AM_USK] = (1u << CP0St_SX),
+ [6] = (1u << CP0St_KX),
+ [CP0SC_AM_UUSK] = (1u << CP0St_UX),
+ };
+ unsigned int am = CP0SC_AM_UK;
+ unsigned int xr = (env->CP0_SegCtl2 & CP0SC2_XR_MASK) >> CP0SC2_XR;
+
+ if (xr & (1 << ((address >> 59) & 0x7))) {
+ am = (env->CP0_SegCtl1 & CP0SC1_XAM_MASK) >> CP0SC1_XAM;
+ }
+ /* Does CP0_Status.KX/SX/UX permit the access mode (am) */
+ if (env->CP0_Status & am_ksux[am]) {
+ ret = get_seg_physical_address(env, physical, prot,
+ real_address, access_type,
+ mmu_idx, am, false, env->PAMask,
+ 0);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0xFFFFFFFF80000000ULL) {
+ /* xkseg */
+ if (kernel_mode && KX &&
+ address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot,
+ real_address, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+#endif
+ } else if (address < KSEG1_BASE) {
+ /* kseg0 */
+ ret = get_segctl_physical_address(env, physical, prot, real_address,
+ access_type, mmu_idx,
+ env->CP0_SegCtl1 >> 16, 0x1FFFFFFF);
+ } else if (address < KSEG2_BASE) {
+ /* kseg1 */
+ ret = get_segctl_physical_address(env, physical, prot, real_address,
+ access_type, mmu_idx,
+ env->CP0_SegCtl1, 0x1FFFFFFF);
+ } else if (address < KSEG3_BASE) {
+ /* sseg (kseg2) */
+ ret = get_segctl_physical_address(env, physical, prot, real_address,
+ access_type, mmu_idx,
+ env->CP0_SegCtl0 >> 16, 0x1FFFFFFF);
+ } else {
+ /*
+ * kseg3
+ * XXX: debug segment is not emulated
+ */
+ ret = get_segctl_physical_address(env, physical, prot, real_address,
+ access_type, mmu_idx,
+ env->CP0_SegCtl0, 0x1FFFFFFF);
+ }
+ return ret;
+}
+
+hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ hwaddr phys_addr;
+ int prot;
+
+ if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
+ cpu_mmu_index(env, false)) != 0) {
+ return -1;
+ }
+ return phys_addr;
+}
diff --git a/target/mips/dsp_helper.c b/target/mips/tcg/dsp_helper.c
index 09b6e5fb15..09b6e5fb15 100644
--- a/target/mips/dsp_helper.c
+++ b/target/mips/tcg/dsp_helper.c
diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c
new file mode 100644
index 0000000000..4fb8b00711
--- /dev/null
+++ b/target/mips/tcg/exception.c
@@ -0,0 +1,167 @@
+/*
+ * MIPS Exceptions processing helpers for QEMU.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "internal.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+
+target_ulong exception_resume_pc(CPUMIPSState *env)
+{
+ target_ulong bad_pc;
+ target_ulong isa_mode;
+
+ isa_mode = !!(env->hflags & MIPS_HFLAG_M16);
+ bad_pc = env->active_tc.PC | isa_mode;
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /*
+ * If the exception was raised from a delay slot, come back to
+ * the jump.
+ */
+ bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
+ }
+
+ return bad_pc;
+}
+
+void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception,
+ int error_code)
+{
+ do_raise_exception_err(env, exception, error_code, 0);
+}
+
+void helper_raise_exception(CPUMIPSState *env, uint32_t exception)
+{
+ do_raise_exception(env, exception, GETPC());
+}
+
+void helper_raise_exception_debug(CPUMIPSState *env)
+{
+ do_raise_exception(env, EXCP_DEBUG, 0);
+}
+
+static void raise_exception(CPUMIPSState *env, uint32_t exception)
+{
+ do_raise_exception(env, exception, 0);
+}
+
+void helper_wait(CPUMIPSState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->halted = 1;
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE);
+ /*
+ * Last instruction in the block, PC was updated before
+ * - no need to recover PC and icount.
+ */
+ raise_exception(env, EXCP_HLT);
+}
+
+void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+
+ env->active_tc.PC = tb->pc;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ env->hflags |= tb->flags & MIPS_HFLAG_BMASK;
+}
+
+bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+
+ if (cpu_mips_hw_interrupts_enabled(env) &&
+ cpu_mips_hw_interrupts_pending(env)) {
+ /* Raise it */
+ cs->exception_index = EXCP_EXT_INTERRUPT;
+ env->error_code = 0;
+ mips_cpu_do_interrupt(cs);
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char * const excp_names[EXCP_LAST + 1] = {
+ [EXCP_RESET] = "reset",
+ [EXCP_SRESET] = "soft reset",
+ [EXCP_DSS] = "debug single step",
+ [EXCP_DINT] = "debug interrupt",
+ [EXCP_NMI] = "non-maskable interrupt",
+ [EXCP_MCHECK] = "machine check",
+ [EXCP_EXT_INTERRUPT] = "interrupt",
+ [EXCP_DFWATCH] = "deferred watchpoint",
+ [EXCP_DIB] = "debug instruction breakpoint",
+ [EXCP_IWATCH] = "instruction fetch watchpoint",
+ [EXCP_AdEL] = "address error load",
+ [EXCP_AdES] = "address error store",
+ [EXCP_TLBF] = "TLB refill",
+ [EXCP_IBE] = "instruction bus error",
+ [EXCP_DBp] = "debug breakpoint",
+ [EXCP_SYSCALL] = "syscall",
+ [EXCP_BREAK] = "break",
+ [EXCP_CpU] = "coprocessor unusable",
+ [EXCP_RI] = "reserved instruction",
+ [EXCP_OVERFLOW] = "arithmetic overflow",
+ [EXCP_TRAP] = "trap",
+ [EXCP_FPE] = "floating point",
+ [EXCP_DDBS] = "debug data break store",
+ [EXCP_DWATCH] = "data watchpoint",
+ [EXCP_LTLBL] = "TLB modify",
+ [EXCP_TLBL] = "TLB load",
+ [EXCP_TLBS] = "TLB store",
+ [EXCP_DBE] = "data bus error",
+ [EXCP_DDBL] = "debug data break load",
+ [EXCP_THREAD] = "thread",
+ [EXCP_MDMX] = "MDMX",
+ [EXCP_C2E] = "precise coprocessor 2",
+ [EXCP_CACHE] = "cache error",
+ [EXCP_TLBXI] = "TLB execute-inhibit",
+ [EXCP_TLBRI] = "TLB read-inhibit",
+ [EXCP_MSADIS] = "MSA disabled",
+ [EXCP_MSAFPE] = "MSA floating point",
+};
+
+const char *mips_exception_name(int32_t exception)
+{
+ if (exception < 0 || exception > EXCP_LAST) {
+ return "unknown";
+ }
+ return excp_names[exception];
+}
+
+void do_raise_exception_err(CPUMIPSState *env, uint32_t exception,
+ int error_code, uintptr_t pc)
+{
+ CPUState *cs = env_cpu(env);
+
+ qemu_log_mask(CPU_LOG_INT, "%s: %d (%s) %d\n",
+ __func__, exception, mips_exception_name(exception),
+ error_code);
+ cs->exception_index = exception;
+ env->error_code = error_code;
+
+ cpu_loop_exit_restore(cs, pc);
+}
diff --git a/target/mips/fpu_helper.c b/target/mips/tcg/fpu_helper.c
index 6dd853259e..8ce56ed7c8 100644
--- a/target/mips/fpu_helper.c
+++ b/target/mips/tcg/fpu_helper.c
@@ -38,14 +38,6 @@
#define FP_TO_INT32_OVERFLOW 0x7fffffff
#define FP_TO_INT64_OVERFLOW 0x7fffffffffffffffULL
-/* convert MIPS rounding mode in FCR31 to IEEE library */
-const FloatRoundMode ieee_rm[4] = {
- float_round_nearest_even,
- float_round_to_zero,
- float_round_up,
- float_round_down
-};
-
target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg)
{
target_ulong arg1 = 0;
diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c
new file mode 100644
index 0000000000..d42812b8a6
--- /dev/null
+++ b/target/mips/tcg/ldst_helper.c
@@ -0,0 +1,288 @@
+/*
+ * MIPS emulation load/store helpers for QEMU.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/memop.h"
+#include "internal.h"
+
+#ifndef CONFIG_USER_ONLY
+
+#define HELPER_LD_ATOMIC(name, insn, almask, do_cast) \
+target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \
+{ \
+ if (arg & almask) { \
+ if (!(env->hflags & MIPS_HFLAG_DM)) { \
+ env->CP0_BadVAddr = arg; \
+ } \
+ do_raise_exception(env, EXCP_AdEL, GETPC()); \
+ } \
+ env->CP0_LLAddr = cpu_mips_translate_address(env, arg, MMU_DATA_LOAD, \
+ GETPC()); \
+ env->lladdr = arg; \
+ env->llval = do_cast cpu_##insn##_mmuidx_ra(env, arg, mem_idx, GETPC()); \
+ return env->llval; \
+}
+HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t))
+#ifdef TARGET_MIPS64
+HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong))
+#endif
+#undef HELPER_LD_ATOMIC
+
+#endif /* !CONFIG_USER_ONLY */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define GET_LMASK(v) ((v) & 3)
+#define GET_OFFSET(addr, offset) (addr + (offset))
+#else
+#define GET_LMASK(v) (((v) & 3) ^ 3)
+#define GET_OFFSET(addr, offset) (addr - (offset))
+#endif
+
+void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
+ int mem_idx)
+{
+ cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC());
+
+ if (GET_LMASK(arg2) <= 2) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK(arg2) <= 1) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK(arg2) == 0) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)arg1,
+ mem_idx, GETPC());
+ }
+}
+
+void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
+ int mem_idx)
+{
+ cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
+
+ if (GET_LMASK(arg2) >= 1) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK(arg2) >= 2) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK(arg2) == 3) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24),
+ mem_idx, GETPC());
+ }
+}
+
+#if defined(TARGET_MIPS64)
+/*
+ * "half" load and stores. We must do the memory access inline,
+ * or fault handling won't work.
+ */
+#ifdef TARGET_WORDS_BIGENDIAN
+#define GET_LMASK64(v) ((v) & 7)
+#else
+#define GET_LMASK64(v) (((v) & 7) ^ 7)
+#endif
+
+void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
+ int mem_idx)
+{
+ cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC());
+
+ if (GET_LMASK64(arg2) <= 6) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 5) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 4) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 3) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 2) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 1) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) <= 0) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, 7), (uint8_t)arg1,
+ mem_idx, GETPC());
+ }
+}
+
+void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
+ int mem_idx)
+{
+ cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
+
+ if (GET_LMASK64(arg2) >= 1) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) >= 2) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) >= 3) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) >= 4) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) >= 5) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) >= 6) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48),
+ mem_idx, GETPC());
+ }
+
+ if (GET_LMASK64(arg2) == 7) {
+ cpu_stb_mmuidx_ra(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56),
+ mem_idx, GETPC());
+ }
+}
+#endif /* TARGET_MIPS64 */
+
+static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 };
+
+void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
+ uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ env->active_tc.gpr[multiple_regs[i]] =
+ (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
+ addr += 4;
+ }
+ }
+
+ if (do_r31) {
+ env->active_tc.gpr[31] =
+ (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
+ }
+}
+
+void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
+ uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
+ mem_idx, GETPC());
+ addr += 4;
+ }
+ }
+
+ if (do_r31) {
+ cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
+ }
+}
+
+#if defined(TARGET_MIPS64)
+void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
+ uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ env->active_tc.gpr[multiple_regs[i]] =
+ cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
+ addr += 8;
+ }
+ }
+
+ if (do_r31) {
+ env->active_tc.gpr[31] =
+ cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
+ }
+}
+
+void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
+ uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
+ mem_idx, GETPC());
+ addr += 8;
+ }
+ }
+
+ if (do_r31) {
+ cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
+ }
+}
+
+#endif /* TARGET_MIPS64 */
diff --git a/target/mips/lmmi_helper.c b/target/mips/tcg/lmmi_helper.c
index abeb7736ae..abeb7736ae 100644
--- a/target/mips/lmmi_helper.c
+++ b/target/mips/tcg/lmmi_helper.c
diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build
new file mode 100644
index 0000000000..5d8acbaf0d
--- /dev/null
+++ b/target/mips/tcg/meson.build
@@ -0,0 +1,35 @@
+gen = [
+ decodetree.process('mips32r6.decode', extra_args: '--static-decode=decode_mips32r6'),
+ decodetree.process('mips64r6.decode', extra_args: '--static-decode=decode_mips64r6'),
+ decodetree.process('msa32.decode', extra_args: '--static-decode=decode_msa32'),
+ decodetree.process('msa64.decode', extra_args: '--static-decode=decode_msa64'),
+ decodetree.process('tx79.decode', extra_args: '--static-decode=decode_tx79'),
+]
+
+mips_ss.add(gen)
+mips_ss.add(files(
+ 'dsp_helper.c',
+ 'exception.c',
+ 'fpu_helper.c',
+ 'ldst_helper.c',
+ 'lmmi_helper.c',
+ 'msa_helper.c',
+ 'msa_translate.c',
+ 'op_helper.c',
+ 'rel6_translate.c',
+ 'translate.c',
+ 'translate_addr_const.c',
+ 'txx9_translate.c',
+))
+mips_ss.add(when: 'TARGET_MIPS64', if_true: files(
+ 'tx79_translate.c',
+), if_false: files(
+ 'mxu_translate.c',
+))
+
+if have_user
+ subdir('user')
+endif
+if have_system
+ subdir('sysemu')
+endif
diff --git a/target/mips/mips32r6.decode b/target/mips/tcg/mips32r6.decode
index 837c991edc..837c991edc 100644
--- a/target/mips/mips32r6.decode
+++ b/target/mips/tcg/mips32r6.decode
diff --git a/target/mips/mips64r6.decode b/target/mips/tcg/mips64r6.decode
index b58d8009cc..b58d8009cc 100644
--- a/target/mips/mips64r6.decode
+++ b/target/mips/tcg/mips64r6.decode
diff --git a/target/mips/msa32.decode b/target/mips/tcg/msa32.decode
index ca200e373b..ca200e373b 100644
--- a/target/mips/msa32.decode
+++ b/target/mips/tcg/msa32.decode
diff --git a/target/mips/msa64.decode b/target/mips/tcg/msa64.decode
index d2442474d0..d2442474d0 100644
--- a/target/mips/msa64.decode
+++ b/target/mips/tcg/msa64.decode
diff --git a/target/mips/msa_helper.c b/target/mips/tcg/msa_helper.c
index 4caefe29ad..04af54f66d 100644
--- a/target/mips/msa_helper.c
+++ b/target/mips/tcg/msa_helper.c
@@ -8595,39 +8595,3 @@ void helper_msa_st_d(CPUMIPSState *env, uint32_t wd,
cpu_stq_data(env, addr + (1 << DF_DOUBLE), pwd->d[1]);
#endif
}
-
-void msa_reset(CPUMIPSState *env)
-{
- if (!ase_msa_available(env)) {
- return;
- }
-
-#ifdef CONFIG_USER_ONLY
- /* MSA access enabled */
- env->CP0_Config5 |= 1 << CP0C5_MSAEn;
- env->CP0_Status |= (1 << CP0St_CU1) | (1 << CP0St_FR);
-#endif
-
- /*
- * MSA CSR:
- * - non-signaling floating point exception mode off (NX bit is 0)
- * - Cause, Enables, and Flags are all 0
- * - round to nearest / ties to even (RM bits are 0)
- */
- env->active_tc.msacsr = 0;
-
- restore_msa_fp_status(env);
-
- /* tininess detected after rounding.*/
- set_float_detect_tininess(float_tininess_after_rounding,
- &env->active_tc.msa_fp_status);
-
- /* clear float_status exception flags */
- set_float_exception_flags(0, &env->active_tc.msa_fp_status);
-
- /* clear float_status nan mode */
- set_default_nan_mode(0, &env->active_tc.msa_fp_status);
-
- /* set proper signanling bit meaning ("1" means "quiet") */
- set_snan_bit_is_one(0, &env->active_tc.msa_fp_status);
-}
diff --git a/target/mips/msa_helper.h.inc b/target/mips/tcg/msa_helper.h.inc
index 4963d1553a..4963d1553a 100644
--- a/target/mips/msa_helper.h.inc
+++ b/target/mips/tcg/msa_helper.h.inc
diff --git a/target/mips/msa_translate.c b/target/mips/tcg/msa_translate.c
index ae6587edf6..ae6587edf6 100644
--- a/target/mips/msa_translate.c
+++ b/target/mips/tcg/msa_translate.c
diff --git a/target/mips/mxu_translate.c b/target/mips/tcg/mxu_translate.c
index fb0a811af6..fb0a811af6 100644
--- a/target/mips/mxu_translate.c
+++ b/target/mips/tcg/mxu_translate.c
diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c
new file mode 100644
index 0000000000..ce1549c985
--- /dev/null
+++ b/target/mips/tcg/op_helper.c
@@ -0,0 +1,420 @@
+/*
+ * MIPS emulation helpers for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "internal.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/memop.h"
+#include "fpu_helper.h"
+
+/* 64 bits arithmetic for 32 bits hosts */
+static inline uint64_t get_HILO(CPUMIPSState *env)
+{
+ return ((uint64_t)(env->active_tc.HI[0]) << 32) |
+ (uint32_t)env->active_tc.LO[0];
+}
+
+static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO)
+{
+ env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
+ return env->active_tc.HI[0] = (int32_t)(HILO >> 32);
+}
+
+static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO)
+{
+ target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
+ env->active_tc.HI[0] = (int32_t)(HILO >> 32);
+ return tmp;
+}
+
+/* Multiplication variants of the vr54xx. */
+target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2));
+}
+
+target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 *
+ (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, (uint64_t)get_HILO(env) +
+ (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (uint64_t)get_HILO(env) +
+ (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HI_LOT0(env, (uint64_t)get_HILO(env) -
+ (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (uint64_t)get_HILO(env) -
+ (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 *
+ (uint64_t)(uint32_t)arg2);
+}
+
+target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 *
+ (int64_t)(int32_t)arg2);
+}
+
+target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1,
+ target_ulong arg2)
+{
+ return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 *
+ (uint64_t)(uint32_t)arg2);
+}
+
+static inline target_ulong bitswap(target_ulong v)
+{
+ v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) |
+ ((v & (target_ulong)0x5555555555555555ULL) << 1);
+ v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) |
+ ((v & (target_ulong)0x3333333333333333ULL) << 2);
+ v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) |
+ ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4);
+ return v;
+}
+
+#ifdef TARGET_MIPS64
+target_ulong helper_dbitswap(target_ulong rt)
+{
+ return bitswap(rt);
+}
+#endif
+
+target_ulong helper_bitswap(target_ulong rt)
+{
+ return (int32_t)bitswap(rt);
+}
+
+target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx,
+ uint32_t stripe)
+{
+ int i;
+ uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff);
+ uint64_t tmp1 = tmp0;
+ for (i = 0; i <= 46; i++) {
+ int s;
+ if (i & 0x8) {
+ s = shift;
+ } else {
+ s = shiftx;
+ }
+
+ if (stripe != 0 && !(i & 0x4)) {
+ s = ~s;
+ }
+ if (s & 0x10) {
+ if (tmp0 & (1LL << (i + 16))) {
+ tmp1 |= 1LL << i;
+ } else {
+ tmp1 &= ~(1LL << i);
+ }
+ }
+ }
+
+ uint64_t tmp2 = tmp1;
+ for (i = 0; i <= 38; i++) {
+ int s;
+ if (i & 0x4) {
+ s = shift;
+ } else {
+ s = shiftx;
+ }
+
+ if (s & 0x8) {
+ if (tmp1 & (1LL << (i + 8))) {
+ tmp2 |= 1LL << i;
+ } else {
+ tmp2 &= ~(1LL << i);
+ }
+ }
+ }
+
+ uint64_t tmp3 = tmp2;
+ for (i = 0; i <= 34; i++) {
+ int s;
+ if (i & 0x2) {
+ s = shift;
+ } else {
+ s = shiftx;
+ }
+ if (s & 0x4) {
+ if (tmp2 & (1LL << (i + 4))) {
+ tmp3 |= 1LL << i;
+ } else {
+ tmp3 &= ~(1LL << i);
+ }
+ }
+ }
+
+ uint64_t tmp4 = tmp3;
+ for (i = 0; i <= 32; i++) {
+ int s;
+ if (i & 0x1) {
+ s = shift;
+ } else {
+ s = shiftx;
+ }
+ if (s & 0x2) {
+ if (tmp3 & (1LL << (i + 2))) {
+ tmp4 |= 1LL << i;
+ } else {
+ tmp4 &= ~(1LL << i);
+ }
+ }
+ }
+
+ uint64_t tmp5 = tmp4;
+ for (i = 0; i <= 31; i++) {
+ int s;
+ s = shift;
+ if (s & 0x1) {
+ if (tmp4 & (1LL << (i + 1))) {
+ tmp5 |= 1LL << i;
+ } else {
+ tmp5 &= ~(1LL << i);
+ }
+ }
+ }
+
+ return (int64_t)(int32_t)(uint32_t)tmp5;
+}
+
+void helper_fork(target_ulong arg1, target_ulong arg2)
+{
+ /*
+ * arg1 = rt, arg2 = rs
+ * TODO: store to TC register
+ */
+}
+
+target_ulong helper_yield(CPUMIPSState *env, target_ulong arg)
+{
+ target_long arg1 = arg;
+
+ if (arg1 < 0) {
+ /* No scheduling policy implemented. */
+ if (arg1 != -2) {
+ if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) &&
+ env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) {
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT;
+ do_raise_exception(env, EXCP_THREAD, GETPC());
+ }
+ }
+ } else if (arg1 == 0) {
+ if (0) {
+ /* TODO: TC underflow */
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ do_raise_exception(env, EXCP_THREAD, GETPC());
+ } else {
+ /* TODO: Deallocate TC */
+ }
+ } else if (arg1 > 0) {
+ /* Yield qualifier inputs not implemented. */
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT;
+ do_raise_exception(env, EXCP_THREAD, GETPC());
+ }
+ return env->CP0_YQMask;
+}
+
+static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc)
+{
+ if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) {
+ return;
+ }
+ do_raise_exception(env, EXCP_RI, pc);
+}
+
+target_ulong helper_rdhwr_cpunum(CPUMIPSState *env)
+{
+ check_hwrena(env, 0, GETPC());
+ return env->CP0_EBase & 0x3ff;
+}
+
+target_ulong helper_rdhwr_synci_step(CPUMIPSState *env)
+{
+ check_hwrena(env, 1, GETPC());
+ return env->SYNCI_Step;
+}
+
+target_ulong helper_rdhwr_cc(CPUMIPSState *env)
+{
+ check_hwrena(env, 2, GETPC());
+#ifdef CONFIG_USER_ONLY
+ return env->CP0_Count;
+#else
+ return (int32_t)cpu_mips_get_count(env);
+#endif
+}
+
+target_ulong helper_rdhwr_ccres(CPUMIPSState *env)
+{
+ check_hwrena(env, 3, GETPC());
+ return env->CCRes;
+}
+
+target_ulong helper_rdhwr_performance(CPUMIPSState *env)
+{
+ check_hwrena(env, 4, GETPC());
+ return env->CP0_Performance0;
+}
+
+target_ulong helper_rdhwr_xnp(CPUMIPSState *env)
+{
+ check_hwrena(env, 5, GETPC());
+ return (env->CP0_Config5 >> CP0C5_XNP) & 1;
+}
+
+void helper_pmon(CPUMIPSState *env, int function)
+{
+ function /= 2;
+ switch (function) {
+ case 2: /* TODO: char inbyte(int waitflag); */
+ if (env->active_tc.gpr[4] == 0) {
+ env->active_tc.gpr[2] = -1;
+ }
+ /* Fall through */
+ case 11: /* TODO: char inbyte (void); */
+ env->active_tc.gpr[2] = -1;
+ break;
+ case 3:
+ case 12:
+ printf("%c", (char)(env->active_tc.gpr[4] & 0xFF));
+ break;
+ case 17:
+ break;
+ case 158:
+ {
+ unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4];
+ printf("%s", fmt);
+ }
+ break;
+ }
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ int error_code = 0;
+ int excp;
+
+ if (!(env->hflags & MIPS_HFLAG_DM)) {
+ env->CP0_BadVAddr = addr;
+ }
+
+ if (access_type == MMU_DATA_STORE) {
+ excp = EXCP_AdES;
+ } else {
+ excp = EXCP_AdEL;
+ if (access_type == MMU_INST_FETCH) {
+ error_code |= EXCP_INST_NOTAVAIL;
+ }
+ }
+
+ do_raise_exception_err(env, excp, error_code, retaddr);
+}
+
+void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+ vaddr addr, unsigned size,
+ MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+
+ if (access_type == MMU_INST_FETCH) {
+ do_raise_exception(env, EXCP_IBE, retaddr);
+ } else {
+ do_raise_exception(env, EXCP_DBE, retaddr);
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/mips/rel6_translate.c b/target/mips/tcg/rel6_translate.c
index 0354370927..0354370927 100644
--- a/target/mips/rel6_translate.c
+++ b/target/mips/tcg/rel6_translate.c
diff --git a/target/mips/cp0_helper.c b/target/mips/tcg/sysemu/cp0_helper.c
index aae2af6ecc..aae2af6ecc 100644
--- a/target/mips/cp0_helper.c
+++ b/target/mips/tcg/sysemu/cp0_helper.c
diff --git a/target/mips/tcg/sysemu/meson.build b/target/mips/tcg/sysemu/meson.build
new file mode 100644
index 0000000000..4da2c577b2
--- /dev/null
+++ b/target/mips/tcg/sysemu/meson.build
@@ -0,0 +1,6 @@
+mips_softmmu_ss.add(files(
+ 'cp0_helper.c',
+ 'mips-semi.c',
+ 'special_helper.c',
+ 'tlb_helper.c',
+))
diff --git a/target/mips/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c
index 6de60fa6dd..6de60fa6dd 100644
--- a/target/mips/mips-semi.c
+++ b/target/mips/tcg/sysemu/mips-semi.c
diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c
new file mode 100644
index 0000000000..2a2afb49e8
--- /dev/null
+++ b/target/mips/tcg/sysemu/special_helper.c
@@ -0,0 +1,173 @@
+/*
+ * QEMU MIPS emulation: Special opcode helpers
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "internal.h"
+
+/* Specials */
+target_ulong helper_di(CPUMIPSState *env)
+{
+ target_ulong t0 = env->CP0_Status;
+
+ env->CP0_Status = t0 & ~(1 << CP0St_IE);
+ return t0;
+}
+
+target_ulong helper_ei(CPUMIPSState *env)
+{
+ target_ulong t0 = env->CP0_Status;
+
+ env->CP0_Status = t0 | (1 << CP0St_IE);
+ return t0;
+}
+
+static void debug_pre_eret(CPUMIPSState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
+ qemu_log("ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
+ env->active_tc.PC, env->CP0_EPC);
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
+ }
+ if (env->hflags & MIPS_HFLAG_DM) {
+ qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
+ }
+ qemu_log("\n");
+ }
+}
+
+static void debug_post_eret(CPUMIPSState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
+ qemu_log(" => PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
+ env->active_tc.PC, env->CP0_EPC);
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
+ }
+ if (env->hflags & MIPS_HFLAG_DM) {
+ qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
+ }
+ switch (cpu_mmu_index(env, false)) {
+ case 3:
+ qemu_log(", ERL\n");
+ break;
+ case MIPS_HFLAG_UM:
+ qemu_log(", UM\n");
+ break;
+ case MIPS_HFLAG_SM:
+ qemu_log(", SM\n");
+ break;
+ case MIPS_HFLAG_KM:
+ qemu_log("\n");
+ break;
+ default:
+ cpu_abort(env_cpu(env), "Invalid MMU mode!\n");
+ break;
+ }
+ }
+}
+
+bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+
+ if ((env->hflags & MIPS_HFLAG_BMASK) != 0
+ && env->active_tc.PC != tb->pc) {
+ env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ return true;
+ }
+ return false;
+}
+
+static inline void exception_return(CPUMIPSState *env)
+{
+ debug_pre_eret(env);
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ mips_env_set_pc(env, env->CP0_ErrorEPC);
+ env->CP0_Status &= ~(1 << CP0St_ERL);
+ } else {
+ mips_env_set_pc(env, env->CP0_EPC);
+ env->CP0_Status &= ~(1 << CP0St_EXL);
+ }
+ compute_hflags(env);
+ debug_post_eret(env);
+}
+
+void helper_eret(CPUMIPSState *env)
+{
+ exception_return(env);
+ env->CP0_LLAddr = 1;
+ env->lladdr = 1;
+}
+
+void helper_eretnc(CPUMIPSState *env)
+{
+ exception_return(env);
+}
+
+void helper_deret(CPUMIPSState *env)
+{
+ debug_pre_eret(env);
+
+ env->hflags &= ~MIPS_HFLAG_DM;
+ compute_hflags(env);
+
+ mips_env_set_pc(env, env->CP0_DEPC);
+
+ debug_post_eret(env);
+}
+
+void helper_cache(CPUMIPSState *env, target_ulong addr, uint32_t op)
+{
+ static const char *const type_name[] = {
+ "Primary Instruction",
+ "Primary Data or Unified Primary",
+ "Tertiary",
+ "Secondary"
+ };
+ uint32_t cache_type = extract32(op, 0, 2);
+ uint32_t cache_operation = extract32(op, 2, 3);
+ target_ulong index = addr & 0x1fffffff;
+
+ switch (cache_operation) {
+ case 0b010: /* Index Store Tag */
+ memory_region_dispatch_write(env->itc_tag, index, env->CP0_TagLo,
+ MO_64, MEMTXATTRS_UNSPECIFIED);
+ break;
+ case 0b001: /* Index Load Tag */
+ memory_region_dispatch_read(env->itc_tag, index, &env->CP0_TagLo,
+ MO_64, MEMTXATTRS_UNSPECIFIED);
+ break;
+ case 0b000: /* Index Invalidate */
+ case 0b100: /* Hit Invalidate */
+ case 0b110: /* Hit Writeback */
+ /* no-op */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "cache operation:%u (type: %s cache)\n",
+ cache_operation, type_name[cache_type]);
+ break;
+ }
+}
diff --git a/target/mips/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c
index 8d3ea49780..259f780d19 100644
--- a/target/mips/tlb_helper.c
+++ b/target/mips/tcg/sysemu/tlb_helper.c
@@ -24,22 +24,341 @@
#include "exec/cpu_ldst.h"
#include "exec/log.h"
#include "hw/mips/cpudevs.h"
+#include "exec/helper-proto.h"
-enum {
- TLBRET_XI = -6,
- TLBRET_RI = -5,
- TLBRET_DIRTY = -4,
- TLBRET_INVALID = -3,
- TLBRET_NOMATCH = -2,
- TLBRET_BADADDR = -1,
- TLBRET_MATCH = 0
-};
+/* TLB management */
+static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first)
+{
+ /* Discard entries from env->tlb[first] onwards. */
+ while (env->tlb->tlb_in_use > first) {
+ r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0);
+ }
+}
+
+static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo)
+{
+#if defined(TARGET_MIPS64)
+ return extract64(entrylo, 6, 54);
+#else
+ return extract64(entrylo, 6, 24) | /* PFN */
+ (extract64(entrylo, 32, 32) << 24); /* PFNX */
+#endif
+}
+
+static void r4k_fill_tlb(CPUMIPSState *env, int idx)
+{
+ r4k_tlb_t *tlb;
+ uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1);
+
+ /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) {
+ tlb->EHINV = 1;
+ return;
+ }
+ tlb->EHINV = 0;
+ tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
+#if defined(TARGET_MIPS64)
+ tlb->VPN &= env->SEGMask;
+#endif
+ tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+ tlb->MMID = env->CP0_MemoryMapID;
+ tlb->PageMask = env->CP0_PageMask;
+ tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
+ tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
+ tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
+ tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
+ tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1;
+ tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1;
+ tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12;
+ tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
+ tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
+ tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
+ tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1;
+ tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1;
+ tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12;
+}
+
+static void r4k_helper_tlbinv(CPUMIPSState *env)
+{
+ bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
+ uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+ uint32_t MMID = env->CP0_MemoryMapID;
+ uint32_t tlb_mmid;
+ r4k_tlb_t *tlb;
+ int idx;
+
+ MMID = mi ? MMID : (uint32_t) ASID;
+ for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
+ if (!tlb->G && tlb_mmid == MMID) {
+ tlb->EHINV = 1;
+ }
+ }
+ cpu_mips_tlb_flush(env);
+}
+
+static void r4k_helper_tlbinvf(CPUMIPSState *env)
+{
+ int idx;
+
+ for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
+ env->tlb->mmu.r4k.tlb[idx].EHINV = 1;
+ }
+ cpu_mips_tlb_flush(env);
+}
+
+static void r4k_helper_tlbwi(CPUMIPSState *env)
+{
+ bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
+ target_ulong VPN;
+ uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+ uint32_t MMID = env->CP0_MemoryMapID;
+ uint32_t tlb_mmid;
+ bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1;
+ r4k_tlb_t *tlb;
+ int idx;
+
+ MMID = mi ? MMID : (uint32_t) ASID;
+
+ idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
+#if defined(TARGET_MIPS64)
+ VPN &= env->SEGMask;
+#endif
+ EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0;
+ G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
+ V0 = (env->CP0_EntryLo0 & 2) != 0;
+ D0 = (env->CP0_EntryLo0 & 4) != 0;
+ XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1;
+ RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1;
+ V1 = (env->CP0_EntryLo1 & 2) != 0;
+ D1 = (env->CP0_EntryLo1 & 4) != 0;
+ XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1;
+ RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1;
+
+ tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
+ /*
+ * Discard cached TLB entries, unless tlbwi is just upgrading access
+ * permissions on the current entry.
+ */
+ if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G ||
+ (!tlb->EHINV && EHINV) ||
+ (tlb->V0 && !V0) || (tlb->D0 && !D0) ||
+ (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) ||
+ (tlb->V1 && !V1) || (tlb->D1 && !D1) ||
+ (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) {
+ r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
+ }
+
+ r4k_invalidate_tlb(env, idx, 0);
+ r4k_fill_tlb(env, idx);
+}
+
+static void r4k_helper_tlbwr(CPUMIPSState *env)
+{
+ int r = cpu_mips_get_random(env);
+
+ r4k_invalidate_tlb(env, r, 1);
+ r4k_fill_tlb(env, r);
+}
+
+static void r4k_helper_tlbp(CPUMIPSState *env)
+{
+ bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
+ r4k_tlb_t *tlb;
+ target_ulong mask;
+ target_ulong tag;
+ target_ulong VPN;
+ uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+ uint32_t MMID = env->CP0_MemoryMapID;
+ uint32_t tlb_mmid;
+ int i;
+
+ MMID = mi ? MMID : (uint32_t) ASID;
+ for (i = 0; i < env->tlb->nb_tlb; i++) {
+ tlb = &env->tlb->mmu.r4k.tlb[i];
+ /* 1k pages are not supported. */
+ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ tag = env->CP0_EntryHi & ~mask;
+ VPN = tlb->VPN & ~mask;
+#if defined(TARGET_MIPS64)
+ tag &= env->SEGMask;
+#endif
+ tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
+ /* Check ASID/MMID, virtual page number & size */
+ if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) {
+ /* TLB match */
+ env->CP0_Index = i;
+ break;
+ }
+ }
+ if (i == env->tlb->nb_tlb) {
+ /* No match. Discard any shadow entries, if any of them match. */
+ for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) {
+ tlb = &env->tlb->mmu.r4k.tlb[i];
+ /* 1k pages are not supported. */
+ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ tag = env->CP0_EntryHi & ~mask;
+ VPN = tlb->VPN & ~mask;
+#if defined(TARGET_MIPS64)
+ tag &= env->SEGMask;
+#endif
+ tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
+ /* Check ASID/MMID, virtual page number & size */
+ if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) {
+ r4k_mips_tlb_flush_extra(env, i);
+ break;
+ }
+ }
+
+ env->CP0_Index |= 0x80000000;
+ }
+}
+
+static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn)
+{
+#if defined(TARGET_MIPS64)
+ return tlb_pfn << 6;
+#else
+ return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */
+ (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */
+#endif
+}
+
+static void r4k_helper_tlbr(CPUMIPSState *env)
+{
+ bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1);
+ uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
+ uint32_t MMID = env->CP0_MemoryMapID;
+ uint32_t tlb_mmid;
+ r4k_tlb_t *tlb;
+ int idx;
+
+ MMID = mi ? MMID : (uint32_t) ASID;
+ idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+
+ tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID;
+ /* If this will change the current ASID/MMID, flush qemu's TLB. */
+ if (MMID != tlb_mmid) {
+ cpu_mips_tlb_flush(env);
+ }
+
+ r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
+
+ if (tlb->EHINV) {
+ env->CP0_EntryHi = 1 << CP0EnHi_EHINV;
+ env->CP0_PageMask = 0;
+ env->CP0_EntryLo0 = 0;
+ env->CP0_EntryLo1 = 0;
+ } else {
+ env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID;
+ env->CP0_MemoryMapID = tlb->MMID;
+ env->CP0_PageMask = tlb->PageMask;
+ env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
+ ((uint64_t)tlb->RI0 << CP0EnLo_RI) |
+ ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) |
+ get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12);
+ env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
+ ((uint64_t)tlb->RI1 << CP0EnLo_RI) |
+ ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) |
+ get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12);
+ }
+}
-#if !defined(CONFIG_USER_ONLY)
+void helper_tlbwi(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbwi(env);
+}
+
+void helper_tlbwr(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbwr(env);
+}
+
+void helper_tlbp(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbp(env);
+}
+
+void helper_tlbr(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbr(env);
+}
+
+void helper_tlbinv(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbinv(env);
+}
+
+void helper_tlbinvf(CPUMIPSState *env)
+{
+ env->tlb->helper_tlbinvf(env);
+}
+
+static void global_invalidate_tlb(CPUMIPSState *env,
+ uint32_t invMsgVPN2,
+ uint8_t invMsgR,
+ uint32_t invMsgMMid,
+ bool invAll,
+ bool invVAMMid,
+ bool invMMid,
+ bool invVA)
+{
+
+ int idx;
+ r4k_tlb_t *tlb;
+ bool VAMatch;
+ bool MMidMatch;
+
+ for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ VAMatch =
+ (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask))
+#ifdef TARGET_MIPS64
+ &&
+ (extract64(env->CP0_EntryHi, 62, 2) == invMsgR)
+#endif
+ );
+ MMidMatch = tlb->MMID == invMsgMMid;
+ if ((invAll && (idx > env->CP0_Wired)) ||
+ (VAMatch && invVAMMid && (tlb->G || MMidMatch)) ||
+ (VAMatch && invVA) ||
+ (MMidMatch && !(tlb->G) && invMMid)) {
+ tlb->EHINV = 1;
+ }
+ }
+ cpu_mips_tlb_flush(env);
+}
+
+void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type)
+{
+ bool invAll = type == 0;
+ bool invVA = type == 1;
+ bool invMMid = type == 2;
+ bool invVAMMid = type == 3;
+ uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1);
+ uint8_t invMsgR = 0;
+ uint32_t invMsgMMid = env->CP0_MemoryMapID;
+ CPUState *other_cs = first_cpu;
+
+#ifdef TARGET_MIPS64
+ invMsgR = extract64(arg, 62, 2);
+#endif
+
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(other_cs);
+ global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid,
+ invAll, invVAMMid, invMMid, invVA);
+ }
+}
/* no MMU emulation */
-int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type)
+static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, MMUAccessType access_type)
{
*physical = address;
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
@@ -47,8 +366,9 @@ int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
}
/* fixed mapping MMU emulation */
-int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type)
+static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical,
+ int *prot, target_ulong address,
+ MMUAccessType access_type)
{
if (address <= (int32_t)0x7FFFFFFFUL) {
if (!(env->CP0_Status & (1 << CP0St_ERL))) {
@@ -67,8 +387,8 @@ int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
}
/* MIPS32/MIPS64 R4000-style MMU emulation */
-int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, MMUAccessType access_type)
+static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, MMUAccessType access_type)
{
uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask;
uint32_t MMID = env->CP0_MemoryMapID;
@@ -166,236 +486,6 @@ void mmu_init(CPUMIPSState *env, const mips_def_t *def)
}
}
-static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx)
-{
- /*
- * Interpret access control mode and mmu_idx.
- * AdE? TLB?
- * AM K S U E K S U E
- * UK 0 0 1 1 0 0 - - 0
- * MK 1 0 1 1 0 1 - - !eu
- * MSK 2 0 0 1 0 1 1 - !eu
- * MUSK 3 0 0 0 0 1 1 1 !eu
- * MUSUK 4 0 0 0 0 0 1 1 0
- * USK 5 0 0 1 0 0 0 - 0
- * - 6 - - - - - - - -
- * UUSK 7 0 0 0 0 0 0 0 0
- */
- int32_t adetlb_mask;
-
- switch (mmu_idx) {
- case 3: /* ERL */
- /* If EU is set, always unmapped */
- if (eu) {
- return 0;
- }
- /* fall through */
- case MIPS_HFLAG_KM:
- /* Never AdE, TLB mapped if AM={1,2,3} */
- adetlb_mask = 0x70000000;
- goto check_tlb;
-
- case MIPS_HFLAG_SM:
- /* AdE if AM={0,1}, TLB mapped if AM={2,3,4} */
- adetlb_mask = 0xc0380000;
- goto check_ade;
-
- case MIPS_HFLAG_UM:
- /* AdE if AM={0,1,2,5}, TLB mapped if AM={3,4} */
- adetlb_mask = 0xe4180000;
- /* fall through */
- check_ade:
- /* does this AM cause AdE in current execution mode */
- if ((adetlb_mask << am) < 0) {
- return TLBRET_BADADDR;
- }
- adetlb_mask <<= 8;
- /* fall through */
- check_tlb:
- /* is this AM mapped in current execution mode */
- return ((adetlb_mask << am) < 0);
- default:
- assert(0);
- return TLBRET_BADADDR;
- };
-}
-
-static int get_seg_physical_address(CPUMIPSState *env, hwaddr *physical,
- int *prot, target_ulong real_address,
- MMUAccessType access_type, int mmu_idx,
- unsigned int am, bool eu,
- target_ulong segmask,
- hwaddr physical_base)
-{
- int mapped = is_seg_am_mapped(am, eu, mmu_idx);
-
- if (mapped < 0) {
- /* is_seg_am_mapped can report TLBRET_BADADDR */
- return mapped;
- } else if (mapped) {
- /* The segment is TLB mapped */
- return env->tlb->map_address(env, physical, prot, real_address,
- access_type);
- } else {
- /* The segment is unmapped */
- *physical = physical_base | (real_address & segmask);
- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
- return TLBRET_MATCH;
- }
-}
-
-static int get_segctl_physical_address(CPUMIPSState *env, hwaddr *physical,
- int *prot, target_ulong real_address,
- MMUAccessType access_type, int mmu_idx,
- uint16_t segctl, target_ulong segmask)
-{
- unsigned int am = (segctl & CP0SC_AM_MASK) >> CP0SC_AM;
- bool eu = (segctl >> CP0SC_EU) & 1;
- hwaddr pa = ((hwaddr)segctl & CP0SC_PA_MASK) << 20;
-
- return get_seg_physical_address(env, physical, prot, real_address,
- access_type, mmu_idx, am, eu, segmask,
- pa & ~(hwaddr)segmask);
-}
-
-static int get_physical_address(CPUMIPSState *env, hwaddr *physical,
- int *prot, target_ulong real_address,
- MMUAccessType access_type, int mmu_idx)
-{
- /* User mode can only access useg/xuseg */
-#if defined(TARGET_MIPS64)
- int user_mode = mmu_idx == MIPS_HFLAG_UM;
- int supervisor_mode = mmu_idx == MIPS_HFLAG_SM;
- int kernel_mode = !user_mode && !supervisor_mode;
- int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
- int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
- int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
-#endif
- int ret = TLBRET_MATCH;
- /* effective address (modified for KVM T&E kernel segments) */
- target_ulong address = real_address;
-
-#define USEG_LIMIT ((target_ulong)(int32_t)0x7FFFFFFFUL)
-#define KSEG0_BASE ((target_ulong)(int32_t)0x80000000UL)
-#define KSEG1_BASE ((target_ulong)(int32_t)0xA0000000UL)
-#define KSEG2_BASE ((target_ulong)(int32_t)0xC0000000UL)
-#define KSEG3_BASE ((target_ulong)(int32_t)0xE0000000UL)
-
-#define KVM_KSEG0_BASE ((target_ulong)(int32_t)0x40000000UL)
-#define KVM_KSEG2_BASE ((target_ulong)(int32_t)0x60000000UL)
-
- if (mips_um_ksegs_enabled()) {
- /* KVM T&E adds guest kernel segments in useg */
- if (real_address >= KVM_KSEG0_BASE) {
- if (real_address < KVM_KSEG2_BASE) {
- /* kseg0 */
- address += KSEG0_BASE - KVM_KSEG0_BASE;
- } else if (real_address <= USEG_LIMIT) {
- /* kseg2/3 */
- address += KSEG2_BASE - KVM_KSEG2_BASE;
- }
- }
- }
-
- if (address <= USEG_LIMIT) {
- /* useg */
- uint16_t segctl;
-
- if (address >= 0x40000000UL) {
- segctl = env->CP0_SegCtl2;
- } else {
- segctl = env->CP0_SegCtl2 >> 16;
- }
- ret = get_segctl_physical_address(env, physical, prot,
- real_address, access_type,
- mmu_idx, segctl, 0x3FFFFFFF);
-#if defined(TARGET_MIPS64)
- } else if (address < 0x4000000000000000ULL) {
- /* xuseg */
- if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
- ret = env->tlb->map_address(env, physical, prot,
- real_address, access_type);
- } else {
- ret = TLBRET_BADADDR;
- }
- } else if (address < 0x8000000000000000ULL) {
- /* xsseg */
- if ((supervisor_mode || kernel_mode) &&
- SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
- ret = env->tlb->map_address(env, physical, prot,
- real_address, access_type);
- } else {
- ret = TLBRET_BADADDR;
- }
- } else if (address < 0xC000000000000000ULL) {
- /* xkphys */
- if ((address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
- /* KX/SX/UX bit to check for each xkphys EVA access mode */
- static const uint8_t am_ksux[8] = {
- [CP0SC_AM_UK] = (1u << CP0St_KX),
- [CP0SC_AM_MK] = (1u << CP0St_KX),
- [CP0SC_AM_MSK] = (1u << CP0St_SX),
- [CP0SC_AM_MUSK] = (1u << CP0St_UX),
- [CP0SC_AM_MUSUK] = (1u << CP0St_UX),
- [CP0SC_AM_USK] = (1u << CP0St_SX),
- [6] = (1u << CP0St_KX),
- [CP0SC_AM_UUSK] = (1u << CP0St_UX),
- };
- unsigned int am = CP0SC_AM_UK;
- unsigned int xr = (env->CP0_SegCtl2 & CP0SC2_XR_MASK) >> CP0SC2_XR;
-
- if (xr & (1 << ((address >> 59) & 0x7))) {
- am = (env->CP0_SegCtl1 & CP0SC1_XAM_MASK) >> CP0SC1_XAM;
- }
- /* Does CP0_Status.KX/SX/UX permit the access mode (am) */
- if (env->CP0_Status & am_ksux[am]) {
- ret = get_seg_physical_address(env, physical, prot,
- real_address, access_type,
- mmu_idx, am, false, env->PAMask,
- 0);
- } else {
- ret = TLBRET_BADADDR;
- }
- } else {
- ret = TLBRET_BADADDR;
- }
- } else if (address < 0xFFFFFFFF80000000ULL) {
- /* xkseg */
- if (kernel_mode && KX &&
- address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
- ret = env->tlb->map_address(env, physical, prot,
- real_address, access_type);
- } else {
- ret = TLBRET_BADADDR;
- }
-#endif
- } else if (address < KSEG1_BASE) {
- /* kseg0 */
- ret = get_segctl_physical_address(env, physical, prot, real_address,
- access_type, mmu_idx,
- env->CP0_SegCtl1 >> 16, 0x1FFFFFFF);
- } else if (address < KSEG2_BASE) {
- /* kseg1 */
- ret = get_segctl_physical_address(env, physical, prot, real_address,
- access_type, mmu_idx,
- env->CP0_SegCtl1, 0x1FFFFFFF);
- } else if (address < KSEG3_BASE) {
- /* sseg (kseg2) */
- ret = get_segctl_physical_address(env, physical, prot, real_address,
- access_type, mmu_idx,
- env->CP0_SegCtl0 >> 16, 0x1FFFFFFF);
- } else {
- /*
- * kseg3
- * XXX: debug segment is not emulated
- */
- ret = get_segctl_physical_address(env, physical, prot, real_address,
- access_type, mmu_idx,
- env->CP0_SegCtl0, 0x1FFFFFFF);
- }
- return ret;
-}
-
void cpu_mips_tlb_flush(CPUMIPSState *env)
{
/* Flush qemu's TLB and discard all shadowed entries. */
@@ -403,8 +493,6 @@ void cpu_mips_tlb_flush(CPUMIPSState *env)
env->tlb->tlb_in_use = env->tlb->nb_tlb;
}
-#endif /* !CONFIG_USER_ONLY */
-
static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
MMUAccessType access_type, int tlb_error)
{
@@ -484,22 +572,6 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
env->error_code = error_code;
}
-#if !defined(CONFIG_USER_ONLY)
-
-hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
- hwaddr phys_addr;
- int prot;
-
- if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD,
- cpu_mmu_index(env, false)) != 0) {
- return -1;
- }
- return phys_addr;
-}
-
#if !defined(TARGET_MIPS64)
/*
@@ -833,7 +905,6 @@ refill:
return true;
}
#endif
-#endif /* !CONFIG_USER_ONLY */
bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
@@ -841,14 +912,11 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
-#if !defined(CONFIG_USER_ONLY)
hwaddr physical;
int prot;
-#endif
int ret = TLBRET_BADADDR;
/* data access */
-#if !defined(CONFIG_USER_ONLY)
/* XXX: put correct access by using cpu_restore_state() correctly */
ret = get_physical_address(env, &physical, &prot, address,
access_type, mmu_idx);
@@ -896,29 +964,28 @@ bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (probe) {
return false;
}
-#endif
raise_mmu_exception(env, address, access_type, ret);
do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr);
}
-#ifndef CONFIG_USER_ONLY
hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address,
- MMUAccessType access_type)
+ MMUAccessType access_type, uintptr_t retaddr)
{
hwaddr physical;
int prot;
int ret = 0;
+ CPUState *cs = env_cpu(env);
/* data access */
ret = get_physical_address(env, &physical, &prot, address, access_type,
cpu_mmu_index(env, false));
- if (ret != TLBRET_MATCH) {
- raise_mmu_exception(env, address, access_type, ret);
- return -1LL;
- } else {
+ if (ret == TLBRET_MATCH) {
return physical;
}
+
+ raise_mmu_exception(env, address, access_type, ret);
+ cpu_loop_exit_restore(cs, retaddr);
}
static void set_hflags_for_handler(CPUMIPSState *env)
@@ -964,11 +1031,8 @@ static inline void set_badinstr_registers(CPUMIPSState *env)
}
}
-#endif /* !CONFIG_USER_ONLY */
-
void mips_cpu_do_interrupt(CPUState *cs)
{
-#if !defined(CONFIG_USER_ONLY)
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
bool update_badinstr = 0;
@@ -1271,11 +1335,9 @@ void mips_cpu_do_interrupt(CPUState *cs)
env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
env->CP0_DEPC);
}
-#endif
cs->exception_index = EXCP_NONE;
}
-#if !defined(CONFIG_USER_ONLY)
void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra)
{
CPUState *cs = env_cpu(env);
@@ -1340,4 +1402,3 @@ void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra)
}
}
}
-#endif /* !CONFIG_USER_ONLY */
diff --git a/target/mips/tcg/sysemu_helper.h.inc b/target/mips/tcg/sysemu_helper.h.inc
new file mode 100644
index 0000000000..4353a966f9
--- /dev/null
+++ b/target/mips/tcg/sysemu_helper.h.inc
@@ -0,0 +1,185 @@
+/*
+ * QEMU MIPS sysemu helpers
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
+ * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support)
+ * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support)
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+DEF_HELPER_1(do_semihosting, void, env)
+
+/* CP0 helpers */
+DEF_HELPER_1(mfc0_mvpcontrol, tl, env)
+DEF_HELPER_1(mfc0_mvpconf0, tl, env)
+DEF_HELPER_1(mfc0_mvpconf1, tl, env)
+DEF_HELPER_1(mftc0_vpecontrol, tl, env)
+DEF_HELPER_1(mftc0_vpeconf0, tl, env)
+DEF_HELPER_1(mfc0_random, tl, env)
+DEF_HELPER_1(mfc0_tcstatus, tl, env)
+DEF_HELPER_1(mftc0_tcstatus, tl, env)
+DEF_HELPER_1(mfc0_tcbind, tl, env)
+DEF_HELPER_1(mftc0_tcbind, tl, env)
+DEF_HELPER_1(mfc0_tcrestart, tl, env)
+DEF_HELPER_1(mftc0_tcrestart, tl, env)
+DEF_HELPER_1(mfc0_tchalt, tl, env)
+DEF_HELPER_1(mftc0_tchalt, tl, env)
+DEF_HELPER_1(mfc0_tccontext, tl, env)
+DEF_HELPER_1(mftc0_tccontext, tl, env)
+DEF_HELPER_1(mfc0_tcschedule, tl, env)
+DEF_HELPER_1(mftc0_tcschedule, tl, env)
+DEF_HELPER_1(mfc0_tcschefback, tl, env)
+DEF_HELPER_1(mftc0_tcschefback, tl, env)
+DEF_HELPER_1(mfc0_count, tl, env)
+DEF_HELPER_1(mfc0_saar, tl, env)
+DEF_HELPER_1(mfhc0_saar, tl, env)
+DEF_HELPER_1(mftc0_entryhi, tl, env)
+DEF_HELPER_1(mftc0_status, tl, env)
+DEF_HELPER_1(mftc0_cause, tl, env)
+DEF_HELPER_1(mftc0_epc, tl, env)
+DEF_HELPER_1(mftc0_ebase, tl, env)
+DEF_HELPER_2(mftc0_configx, tl, env, tl)
+DEF_HELPER_1(mfc0_lladdr, tl, env)
+DEF_HELPER_1(mfc0_maar, tl, env)
+DEF_HELPER_1(mfhc0_maar, tl, env)
+DEF_HELPER_2(mfc0_watchlo, tl, env, i32)
+DEF_HELPER_2(mfc0_watchhi, tl, env, i32)
+DEF_HELPER_2(mfhc0_watchhi, tl, env, i32)
+DEF_HELPER_1(mfc0_debug, tl, env)
+DEF_HELPER_1(mftc0_debug, tl, env)
+#ifdef TARGET_MIPS64
+DEF_HELPER_1(dmfc0_tcrestart, tl, env)
+DEF_HELPER_1(dmfc0_tchalt, tl, env)
+DEF_HELPER_1(dmfc0_tccontext, tl, env)
+DEF_HELPER_1(dmfc0_tcschedule, tl, env)
+DEF_HELPER_1(dmfc0_tcschefback, tl, env)
+DEF_HELPER_1(dmfc0_lladdr, tl, env)
+DEF_HELPER_1(dmfc0_maar, tl, env)
+DEF_HELPER_2(dmfc0_watchlo, tl, env, i32)
+DEF_HELPER_2(dmfc0_watchhi, tl, env, i32)
+DEF_HELPER_1(dmfc0_saar, tl, env)
+#endif /* TARGET_MIPS64 */
+
+DEF_HELPER_2(mtc0_index, void, env, tl)
+DEF_HELPER_2(mtc0_mvpcontrol, void, env, tl)
+DEF_HELPER_2(mtc0_vpecontrol, void, env, tl)
+DEF_HELPER_2(mttc0_vpecontrol, void, env, tl)
+DEF_HELPER_2(mtc0_vpeconf0, void, env, tl)
+DEF_HELPER_2(mttc0_vpeconf0, void, env, tl)
+DEF_HELPER_2(mtc0_vpeconf1, void, env, tl)
+DEF_HELPER_2(mtc0_yqmask, void, env, tl)
+DEF_HELPER_2(mtc0_vpeopt, void, env, tl)
+DEF_HELPER_2(mtc0_entrylo0, void, env, tl)
+DEF_HELPER_2(mtc0_tcstatus, void, env, tl)
+DEF_HELPER_2(mttc0_tcstatus, void, env, tl)
+DEF_HELPER_2(mtc0_tcbind, void, env, tl)
+DEF_HELPER_2(mttc0_tcbind, void, env, tl)
+DEF_HELPER_2(mtc0_tcrestart, void, env, tl)
+DEF_HELPER_2(mttc0_tcrestart, void, env, tl)
+DEF_HELPER_2(mtc0_tchalt, void, env, tl)
+DEF_HELPER_2(mttc0_tchalt, void, env, tl)
+DEF_HELPER_2(mtc0_tccontext, void, env, tl)
+DEF_HELPER_2(mttc0_tccontext, void, env, tl)
+DEF_HELPER_2(mtc0_tcschedule, void, env, tl)
+DEF_HELPER_2(mttc0_tcschedule, void, env, tl)
+DEF_HELPER_2(mtc0_tcschefback, void, env, tl)
+DEF_HELPER_2(mttc0_tcschefback, void, env, tl)
+DEF_HELPER_2(mtc0_entrylo1, void, env, tl)
+DEF_HELPER_2(mtc0_context, void, env, tl)
+DEF_HELPER_2(mtc0_memorymapid, void, env, tl)
+DEF_HELPER_2(mtc0_pagemask, void, env, tl)
+DEF_HELPER_2(mtc0_pagegrain, void, env, tl)
+DEF_HELPER_2(mtc0_segctl0, void, env, tl)
+DEF_HELPER_2(mtc0_segctl1, void, env, tl)
+DEF_HELPER_2(mtc0_segctl2, void, env, tl)
+DEF_HELPER_2(mtc0_pwfield, void, env, tl)
+DEF_HELPER_2(mtc0_pwsize, void, env, tl)
+DEF_HELPER_2(mtc0_wired, void, env, tl)
+DEF_HELPER_2(mtc0_srsconf0, void, env, tl)
+DEF_HELPER_2(mtc0_srsconf1, void, env, tl)
+DEF_HELPER_2(mtc0_srsconf2, void, env, tl)
+DEF_HELPER_2(mtc0_srsconf3, void, env, tl)
+DEF_HELPER_2(mtc0_srsconf4, void, env, tl)
+DEF_HELPER_2(mtc0_hwrena, void, env, tl)
+DEF_HELPER_2(mtc0_pwctl, void, env, tl)
+DEF_HELPER_2(mtc0_count, void, env, tl)
+DEF_HELPER_2(mtc0_saari, void, env, tl)
+DEF_HELPER_2(mtc0_saar, void, env, tl)
+DEF_HELPER_2(mthc0_saar, void, env, tl)
+DEF_HELPER_2(mtc0_entryhi, void, env, tl)
+DEF_HELPER_2(mttc0_entryhi, void, env, tl)
+DEF_HELPER_2(mtc0_compare, void, env, tl)
+DEF_HELPER_2(mtc0_status, void, env, tl)
+DEF_HELPER_2(mttc0_status, void, env, tl)
+DEF_HELPER_2(mtc0_intctl, void, env, tl)
+DEF_HELPER_2(mtc0_srsctl, void, env, tl)
+DEF_HELPER_2(mtc0_cause, void, env, tl)
+DEF_HELPER_2(mttc0_cause, void, env, tl)
+DEF_HELPER_2(mtc0_ebase, void, env, tl)
+DEF_HELPER_2(mttc0_ebase, void, env, tl)
+DEF_HELPER_2(mtc0_config0, void, env, tl)
+DEF_HELPER_2(mtc0_config2, void, env, tl)
+DEF_HELPER_2(mtc0_config3, void, env, tl)
+DEF_HELPER_2(mtc0_config4, void, env, tl)
+DEF_HELPER_2(mtc0_config5, void, env, tl)
+DEF_HELPER_2(mtc0_lladdr, void, env, tl)
+DEF_HELPER_2(mtc0_maar, void, env, tl)
+DEF_HELPER_2(mthc0_maar, void, env, tl)
+DEF_HELPER_2(mtc0_maari, void, env, tl)
+DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32)
+DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32)
+DEF_HELPER_3(mthc0_watchhi, void, env, tl, i32)
+DEF_HELPER_2(mtc0_xcontext, void, env, tl)
+DEF_HELPER_2(mtc0_framemask, void, env, tl)
+DEF_HELPER_2(mtc0_debug, void, env, tl)
+DEF_HELPER_2(mttc0_debug, void, env, tl)
+DEF_HELPER_2(mtc0_performance0, void, env, tl)
+DEF_HELPER_2(mtc0_errctl, void, env, tl)
+DEF_HELPER_2(mtc0_taglo, void, env, tl)
+DEF_HELPER_2(mtc0_datalo, void, env, tl)
+DEF_HELPER_2(mtc0_taghi, void, env, tl)
+DEF_HELPER_2(mtc0_datahi, void, env, tl)
+
+#if defined(TARGET_MIPS64)
+DEF_HELPER_2(dmtc0_entrylo0, void, env, i64)
+DEF_HELPER_2(dmtc0_entrylo1, void, env, i64)
+#endif
+
+/* MIPS MT functions */
+DEF_HELPER_2(mftgpr, tl, env, i32)
+DEF_HELPER_2(mftlo, tl, env, i32)
+DEF_HELPER_2(mfthi, tl, env, i32)
+DEF_HELPER_2(mftacx, tl, env, i32)
+DEF_HELPER_1(mftdsp, tl, env)
+DEF_HELPER_3(mttgpr, void, env, tl, i32)
+DEF_HELPER_3(mttlo, void, env, tl, i32)
+DEF_HELPER_3(mtthi, void, env, tl, i32)
+DEF_HELPER_3(mttacx, void, env, tl, i32)
+DEF_HELPER_2(mttdsp, void, env, tl)
+DEF_HELPER_0(dmt, tl)
+DEF_HELPER_0(emt, tl)
+DEF_HELPER_1(dvpe, tl, env)
+DEF_HELPER_1(evpe, tl, env)
+
+/* R6 Multi-threading */
+DEF_HELPER_1(dvp, tl, env)
+DEF_HELPER_1(evp, tl, env)
+
+/* TLB */
+DEF_HELPER_1(tlbwi, void, env)
+DEF_HELPER_1(tlbwr, void, env)
+DEF_HELPER_1(tlbp, void, env)
+DEF_HELPER_1(tlbr, void, env)
+DEF_HELPER_1(tlbinv, void, env)
+DEF_HELPER_1(tlbinvf, void, env)
+DEF_HELPER_3(ginvt, void, env, tl, i32)
+
+/* Special */
+DEF_HELPER_1(di, tl, env)
+DEF_HELPER_1(ei, tl, env)
+DEF_HELPER_1(eret, void, env)
+DEF_HELPER_1(eretnc, void, env)
+DEF_HELPER_1(deret, void, env)
+DEF_HELPER_3(cache, void, env, tl, i32)
diff --git a/target/mips/tcg/tcg-internal.h b/target/mips/tcg/tcg-internal.h
new file mode 100644
index 0000000000..81b14eb219
--- /dev/null
+++ b/target/mips/tcg/tcg-internal.h
@@ -0,0 +1,64 @@
+/*
+ * MIPS internal definitions and helpers (TCG accelerator)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef MIPS_TCG_INTERNAL_H
+#define MIPS_TCG_INTERNAL_H
+
+#include "tcg/tcg.h"
+#include "exec/memattrs.h"
+#include "hw/core/cpu.h"
+#include "cpu.h"
+
+void mips_tcg_init(void);
+
+void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb);
+void mips_cpu_do_interrupt(CPUState *cpu);
+bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req);
+bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr);
+
+const char *mips_exception_name(int32_t exception);
+
+void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, uint32_t exception,
+ int error_code, uintptr_t pc);
+
+static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
+ uint32_t exception,
+ uintptr_t pc)
+{
+ do_raise_exception_err(env, exception, 0, pc);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+void mmu_init(CPUMIPSState *env, const mips_def_t *def);
+
+void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask);
+
+void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra);
+uint32_t cpu_mips_get_random(CPUMIPSState *env);
+
+bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb);
+
+hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address,
+ MMUAccessType access_type, uintptr_t retaddr);
+void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
+ vaddr addr, unsigned size,
+ MMUAccessType access_type,
+ int mmu_idx, MemTxAttrs attrs,
+ MemTxResult response, uintptr_t retaddr);
+void cpu_mips_tlb_flush(CPUMIPSState *env);
+
+#endif /* !CONFIG_USER_ONLY */
+
+#endif
diff --git a/target/mips/translate.c b/target/mips/tcg/translate.c
index 71fa5ec197..c03a8ae1fe 100644
--- a/target/mips/translate.c
+++ b/target/mips/tcg/translate.c
@@ -39,6 +39,19 @@
#include "fpu_helper.h"
#include "translate.h"
+/*
+ * Many sysemu-only helpers are not reachable for user-only.
+ * Define stub generators here, so that we need not either sprinkle
+ * ifdefs through the translator, nor provide the helper function.
+ */
+#define STUB_HELPER(NAME, ...) \
+ static inline void gen_helper_##NAME(__VA_ARGS__) \
+ { g_assert_not_reached(); }
+
+#ifdef CONFIG_USER_ONLY
+STUB_HELPER(cache, TCGv_env env, TCGv val, TCGv_i32 reg)
+#endif
+
enum {
/* indirect opcode tables */
OPC_SPECIAL = (0x00 << 26),
@@ -1267,13 +1280,6 @@ TCGv_i64 fpu_f64[32];
#define DISAS_STOP DISAS_TARGET_0
#define DISAS_EXIT DISAS_TARGET_1
-static const char * const regnames[] = {
- "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
- "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
- "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
- "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra",
-};
-
static const char * const regnames_HI[] = {
"HI0", "HI1", "HI2", "HI3",
};
@@ -1282,13 +1288,6 @@ static const char * const regnames_LO[] = {
"LO0", "LO1", "LO2", "LO3",
};
-static const char * const fregnames[] = {
- "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
- "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
- "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
- "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
-};
-
/* General purpose registers moves. */
void gen_load_gpr(TCGv t, int reg)
{
@@ -1572,11 +1571,13 @@ void gen_move_high32(TCGv ret, TCGv_i64 arg)
#endif
}
-void check_cp0_enabled(DisasContext *ctx)
+bool check_cp0_enabled(DisasContext *ctx)
{
if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) {
generate_exception_end(ctx, EXCP_CpU);
+ return false;
}
+ return true;
}
void check_cp1_enabled(DisasContext *ctx)
@@ -5945,6 +5946,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
goto cp0_unimplemented;
}
trace_mips_translate_c0("mthc0", register_name, reg, sel);
+ return;
cp0_unimplemented:
qemu_log_mask(LOG_UNIMP, "mthc0 %s (reg %d sel %d)\n",
@@ -18969,9 +18971,11 @@ static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx)
}
break;
case NM_RDPGPR:
+ check_cp0_enabled(ctx);
gen_load_srsgpr(rs, rt);
break;
case NM_WRPGPR:
+ check_cp0_enabled(ctx);
gen_store_srsgpr(rs, rt);
break;
case NM_WAIT:
@@ -20957,6 +20961,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx)
gen_ld(ctx, OPC_LHUE, rt, rs, s);
break;
case NM_CACHEE:
+ check_eva(ctx);
+ check_cp0_enabled(ctx);
check_nms_dl_il_sl_tl_l2c(ctx);
gen_cache_operation(ctx, rt, rs, s);
break;
@@ -24530,11 +24536,11 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx)
gen_st_cond(ctx, rt, rs, imm, MO_TESL, true);
return;
case OPC_CACHEE:
+ check_eva(ctx);
check_cp0_enabled(ctx);
if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) {
gen_cache_operation(ctx, rt, rs, imm);
}
- /* Treat as NOP. */
return;
case OPC_PREFE:
check_cp0_enabled(ctx);
@@ -25593,83 +25599,6 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
translator_loop(&mips_tr_ops, &ctx.base, cs, tb, max_insns);
}
-static void fpu_dump_state(CPUMIPSState *env, FILE * f, int flags)
-{
- int i;
- int is_fpu64 = !!(env->hflags & MIPS_HFLAG_F64);
-
-#define printfpr(fp) \
- do { \
- if (is_fpu64) \
- qemu_fprintf(f, "w:%08x d:%016" PRIx64 \
- " fd:%13g fs:%13g psu: %13g\n", \
- (fp)->w[FP_ENDIAN_IDX], (fp)->d, \
- (double)(fp)->fd, \
- (double)(fp)->fs[FP_ENDIAN_IDX], \
- (double)(fp)->fs[!FP_ENDIAN_IDX]); \
- else { \
- fpr_t tmp; \
- tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \
- tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \
- qemu_fprintf(f, "w:%08x d:%016" PRIx64 \
- " fd:%13g fs:%13g psu:%13g\n", \
- tmp.w[FP_ENDIAN_IDX], tmp.d, \
- (double)tmp.fd, \
- (double)tmp.fs[FP_ENDIAN_IDX], \
- (double)tmp.fs[!FP_ENDIAN_IDX]); \
- } \
- } while (0)
-
-
- qemu_fprintf(f,
- "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%02x\n",
- env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64,
- get_float_exception_flags(&env->active_fpu.fp_status));
- for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) {
- qemu_fprintf(f, "%3s: ", fregnames[i]);
- printfpr(&env->active_fpu.fpr[i]);
- }
-
-#undef printfpr
-}
-
-void mips_cpu_dump_state(CPUState *cs, FILE *f, int flags)
-{
- MIPSCPU *cpu = MIPS_CPU(cs);
- CPUMIPSState *env = &cpu->env;
- int i;
-
- qemu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx
- " LO=0x" TARGET_FMT_lx " ds %04x "
- TARGET_FMT_lx " " TARGET_FMT_ld "\n",
- env->active_tc.PC, env->active_tc.HI[0], env->active_tc.LO[0],
- env->hflags, env->btarget, env->bcond);
- for (i = 0; i < 32; i++) {
- if ((i & 3) == 0) {
- qemu_fprintf(f, "GPR%02d:", i);
- }
- qemu_fprintf(f, " %s " TARGET_FMT_lx,
- regnames[i], env->active_tc.gpr[i]);
- if ((i & 3) == 3) {
- qemu_fprintf(f, "\n");
- }
- }
-
- qemu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x"
- TARGET_FMT_lx "\n",
- env->CP0_Status, env->CP0_Cause, env->CP0_EPC);
- qemu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%016"
- PRIx64 "\n",
- env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr);
- qemu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n",
- env->CP0_Config2, env->CP0_Config3);
- qemu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n",
- env->CP0_Config4, env->CP0_Config5);
- if ((flags & CPU_DUMP_FPU) && (env->hflags & MIPS_HFLAG_FPU)) {
- fpu_dump_state(env, f, flags);
- }
-}
-
void mips_tcg_init(void)
{
int i;
diff --git a/target/mips/translate_addr_const.c b/target/mips/tcg/translate_addr_const.c
index 96f483418e..96f483418e 100644
--- a/target/mips/translate_addr_const.c
+++ b/target/mips/tcg/translate_addr_const.c
diff --git a/target/mips/tx79.decode b/target/mips/tcg/tx79.decode
index 0f748b53a6..0f748b53a6 100644
--- a/target/mips/tx79.decode
+++ b/target/mips/tcg/tx79.decode
diff --git a/target/mips/tx79_translate.c b/target/mips/tcg/tx79_translate.c
index ad83774b97..ad83774b97 100644
--- a/target/mips/tx79_translate.c
+++ b/target/mips/tcg/tx79_translate.c
diff --git a/target/mips/txx9_translate.c b/target/mips/tcg/txx9_translate.c
index 8a2c0b766b..8a2c0b766b 100644
--- a/target/mips/txx9_translate.c
+++ b/target/mips/tcg/txx9_translate.c
diff --git a/target/mips/tcg/user/meson.build b/target/mips/tcg/user/meson.build
new file mode 100644
index 0000000000..79badcd321
--- /dev/null
+++ b/target/mips/tcg/user/meson.build
@@ -0,0 +1,3 @@
+mips_user_ss.add(files(
+ 'tlb_helper.c',
+))
diff --git a/target/mips/tcg/user/tlb_helper.c b/target/mips/tcg/user/tlb_helper.c
new file mode 100644
index 0000000000..b835144b82
--- /dev/null
+++ b/target/mips/tcg/user/tlb_helper.c
@@ -0,0 +1,64 @@
+/*
+ * MIPS TLB (Translation lookaside buffer) helpers.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "internal.h"
+
+static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
+ MMUAccessType access_type)
+{
+ CPUState *cs = env_cpu(env);
+
+ env->error_code = 0;
+ if (access_type == MMU_INST_FETCH) {
+ env->error_code |= EXCP_INST_NOTAVAIL;
+ }
+
+ /* Reference to kernel address from user mode or supervisor mode */
+ /* Reference to supervisor address from user mode */
+ if (access_type == MMU_DATA_STORE) {
+ cs->exception_index = EXCP_AdES;
+ } else {
+ cs->exception_index = EXCP_AdEL;
+ }
+
+ /* Raise exception */
+ if (!(env->hflags & MIPS_HFLAG_DM)) {
+ env->CP0_BadVAddr = address;
+ }
+}
+
+bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+
+ /* data access */
+ raise_mmu_exception(env, address, access_type);
+ do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr);
+}
+
+void mips_cpu_do_interrupt(CPUState *cs)
+{
+ cs->exception_index = EXCP_NONE;
+}
diff --git a/target/mips/translate.h b/target/mips/translate.h
index 2b3c7a69ec..6144259034 100644
--- a/target/mips/translate.h
+++ b/target/mips/translate.h
@@ -120,7 +120,12 @@ void gen_reserved_instruction(DisasContext *ctx);
void check_insn(DisasContext *ctx, uint64_t flags);
void check_mips_64(DisasContext *ctx);
-void check_cp0_enabled(DisasContext *ctx);
+/**
+ * check_cp0_enabled:
+ * Return %true if CP0 is enabled, otherwise return %false
+ * and emit a 'coprocessor unusable' exception.
+ */
+bool check_cp0_enabled(DisasContext *ctx);
void check_cp1_enabled(DisasContext *ctx);
void check_cp1_64bitmode(DisasContext *ctx);
void check_cp1_registers(DisasContext *ctx, int regs);
diff --git a/tests/decode/succ_argset_type1.decode b/tests/decode/succ_argset_type1.decode
new file mode 100644
index 0000000000..ed946b420d
--- /dev/null
+++ b/tests/decode/succ_argset_type1.decode
@@ -0,0 +1 @@
+&asdf b:bool c:uint64_t a
diff --git a/tests/qapi-schema/alternate-data-invalid.err b/tests/qapi-schema/alternate-data-invalid.err
new file mode 100644
index 0000000000..55f1033aef
--- /dev/null
+++ b/tests/qapi-schema/alternate-data-invalid.err
@@ -0,0 +1,2 @@
+alternate-data-invalid.json: In alternate 'Alt':
+alternate-data-invalid.json:2: 'data' must be an object
diff --git a/tests/qapi-schema/alternate-data-invalid.json b/tests/qapi-schema/alternate-data-invalid.json
new file mode 100644
index 0000000000..7d5d905581
--- /dev/null
+++ b/tests/qapi-schema/alternate-data-invalid.json
@@ -0,0 +1,4 @@
+# Alternate type requires an object for 'data'
+{ 'alternate': 'Alt',
+ 'data': ['rubbish', 'nonsense']
+}
diff --git a/tests/qapi-schema/alternate-data-invalid.out b/tests/qapi-schema/alternate-data-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/alternate-data-invalid.out
diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build
index 8ba6917132..d7163e6601 100644
--- a/tests/qapi-schema/meson.build
+++ b/tests/qapi-schema/meson.build
@@ -14,6 +14,7 @@ schemas = [
'alternate-conflict-string.json',
'alternate-conflict-bool-string.json',
'alternate-conflict-num-string.json',
+ 'alternate-data-invalid.json',
'alternate-empty.json',
'alternate-invalid-dict.json',
'alternate-nested.json',
@@ -192,6 +193,7 @@ schemas = [
'union-clash-branches.json',
'union-empty.json',
'union-invalid-base.json',
+ 'union-invalid-data.json',
'union-optional-branch.json',
'union-unknown.json',
'unknown-escape.json',
diff --git a/tests/qapi-schema/union-invalid-data.err b/tests/qapi-schema/union-invalid-data.err
new file mode 100644
index 0000000000..e26cf769a3
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-data.err
@@ -0,0 +1,2 @@
+union-invalid-data.json: In union 'TestUnion':
+union-invalid-data.json:2: 'data' must be an object
diff --git a/tests/qapi-schema/union-invalid-data.json b/tests/qapi-schema/union-invalid-data.json
new file mode 100644
index 0000000000..395ba24d39
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-data.json
@@ -0,0 +1,6 @@
+# the union data type must be an object.
+{ 'union': 'TestUnion',
+ 'base': 'int',
+ 'discriminator': 'int',
+ 'data': ['rubbish', 'nonsense']
+}
diff --git a/tests/qapi-schema/union-invalid-data.out b/tests/qapi-schema/union-invalid-data.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/qapi-schema/union-invalid-data.out
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
index 11104b9208..fc5297e268 100755
--- a/tests/qemu-iotests/245
+++ b/tests/qemu-iotests/245
@@ -905,7 +905,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# We can't reopen hd1 to read-only, as block-stream requires it to be
# read-write
self.reopen(opts['backing'], {'read-only': True},
- "Cannot make block node read-only, there is a writer on it")
+ "Read-only block node 'hd1' cannot support read-write users")
# We can't remove hd2 while the stream job is ongoing
opts['backing']['backing'] = None
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
index 37c35058ae..97e62a4c94 100644
--- a/tests/qemu-iotests/283.out
+++ b/tests/qemu-iotests/283.out
@@ -5,7 +5,7 @@
{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
{"return": {}}
{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
-{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
+{"error": {"class": "GenericError", "desc": "Cannot append backup-top filter: Conflicts with use by source as 'image', which does not allow 'write' on base"}}
=== backup-top should be gone after job-finalize ===
diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out
index 5f41491e05..9f52255da8 100644
--- a/tests/qemu-iotests/tests/qsd-jobs.out
+++ b/tests/qemu-iotests/tests/qsd-jobs.out
@@ -16,7 +16,7 @@ QMP_VERSION
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
-{"error": {"class": "GenericError", "desc": "Conflicts with use by a block device as 'root', which uses 'write' on fmt_base"}}
+{"error": {"class": "GenericError", "desc": "Conflicts with use by stream job 'job0' as 'intermediate node', which does not allow 'write' on fmt_base"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}}
*** done
diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target
index 05b2622bfc..928357b10a 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -37,7 +37,7 @@ AARCH64_TESTS += bti-2
# MTE Tests
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_MTE),)
-AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-6
+AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6
mte-%: CFLAGS += -march=armv8.5-a+memtag
endif
diff --git a/tests/tcg/aarch64/mte-5.c b/tests/tcg/aarch64/mte-5.c
new file mode 100644
index 0000000000..6dbd6ab3ea
--- /dev/null
+++ b/tests/tcg/aarch64/mte-5.c
@@ -0,0 +1,44 @@
+/*
+ * Memory tagging, faulting unaligned access.
+ *
+ * Copyright (c) 2021 Linaro Ltd
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "mte.h"
+
+void pass(int sig, siginfo_t *info, void *uc)
+{
+ assert(info->si_code == SEGV_MTESERR);
+ exit(0);
+}
+
+int main(int ac, char **av)
+{
+ struct sigaction sa;
+ void *p0, *p1, *p2;
+ long excl = 1;
+
+ enable_mte(PR_MTE_TCF_SYNC);
+ p0 = alloc_mte_mem(sizeof(*p0));
+
+ /* Create two differently tagged pointers. */
+ asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(excl));
+ asm("gmi %0,%1,%0" : "+r"(excl) : "r" (p1));
+ assert(excl != 1);
+ asm("irg %0,%1,%2" : "=r"(p2) : "r"(p0), "r"(excl));
+ assert(p1 != p2);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = pass;
+ sa.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &sa, NULL);
+
+ /* Store store two different tags in sequential granules. */
+ asm("stg %0, [%0]" : : "r"(p1));
+ asm("stg %0, [%0]" : : "r"(p2 + 16));
+
+ /* Perform an unaligned load crossing the granules. */
+ asm volatile("ldr %0, [%1]" : "=r"(p0) : "r"(p1 + 12));
+ abort();
+}
diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target
index 616af697fe..0992787d50 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -28,6 +28,7 @@ endif
CFLAGS += -Wno-incompatible-pointer-types -Wno-undefined-internal
+CFLAGS += -fno-unroll-loops
HEX_SRC=$(SRC_PATH)/tests/tcg/hexagon
VPATH += $(HEX_SRC)
@@ -39,7 +40,12 @@ HEX_TESTS = first
HEX_TESTS += misc
HEX_TESTS += preg_alias
HEX_TESTS += dual_stores
+HEX_TESTS += multi_result
HEX_TESTS += mem_noshuf
+HEX_TESTS += circ
+HEX_TESTS += brev
+HEX_TESTS += load_unpack
+HEX_TESTS += load_align
HEX_TESTS += atomics
HEX_TESTS += fpstuff
diff --git a/tests/tcg/hexagon/brev.c b/tests/tcg/hexagon/brev.c
new file mode 100644
index 0000000000..9736a2405d
--- /dev/null
+++ b/tests/tcg/hexagon/brev.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+int err;
+
+#define NBITS 8
+#define SIZE (1 << NBITS)
+
+long long dbuf[SIZE] __attribute__((aligned(1 << 16))) = {0};
+int wbuf[SIZE] __attribute__((aligned(1 << 16))) = {0};
+short hbuf[SIZE] __attribute__((aligned(1 << 16))) = {0};
+unsigned char bbuf[SIZE] __attribute__((aligned(1 << 16))) = {0};
+
+/*
+ * We use the C preporcessor to deal with the combinations of types
+ */
+
+#define BREV_LOAD(SZ, RES, ADDR, INC) \
+ __asm__( \
+ "m0 = %2\n\t" \
+ "%0 = mem" #SZ "(%1++m0:brev)\n\t" \
+ : "=r"(RES), "+r"(ADDR) \
+ : "r"(INC) \
+ : "m0")
+
+#define BREV_LOAD_b(RES, ADDR, INC) \
+ BREV_LOAD(b, RES, ADDR, INC)
+#define BREV_LOAD_ub(RES, ADDR, INC) \
+ BREV_LOAD(ub, RES, ADDR, INC)
+#define BREV_LOAD_h(RES, ADDR, INC) \
+ BREV_LOAD(h, RES, ADDR, INC)
+#define BREV_LOAD_uh(RES, ADDR, INC) \
+ BREV_LOAD(uh, RES, ADDR, INC)
+#define BREV_LOAD_w(RES, ADDR, INC) \
+ BREV_LOAD(w, RES, ADDR, INC)
+#define BREV_LOAD_d(RES, ADDR, INC) \
+ BREV_LOAD(d, RES, ADDR, INC)
+
+#define BREV_STORE(SZ, PART, ADDR, VAL, INC) \
+ __asm__( \
+ "m0 = %2\n\t" \
+ "mem" #SZ "(%0++m0:brev) = %1" PART "\n\t" \
+ : "+r"(ADDR) \
+ : "r"(VAL), "r"(INC) \
+ : "m0", "memory")
+
+#define BREV_STORE_b(ADDR, VAL, INC) \
+ BREV_STORE(b, "", ADDR, VAL, INC)
+#define BREV_STORE_h(ADDR, VAL, INC) \
+ BREV_STORE(h, "", ADDR, VAL, INC)
+#define BREV_STORE_f(ADDR, VAL, INC) \
+ BREV_STORE(h, ".H", ADDR, VAL, INC)
+#define BREV_STORE_w(ADDR, VAL, INC) \
+ BREV_STORE(w, "", ADDR, VAL, INC)
+#define BREV_STORE_d(ADDR, VAL, INC) \
+ BREV_STORE(d, "", ADDR, VAL, INC)
+
+#define BREV_STORE_NEW(SZ, ADDR, VAL, INC) \
+ __asm__( \
+ "m0 = %2\n\t" \
+ "{\n\t" \
+ " r5 = %1\n\t" \
+ " mem" #SZ "(%0++m0:brev) = r5.new\n\t" \
+ "}\n\t" \
+ : "+r"(ADDR) \
+ : "r"(VAL), "r"(INC) \
+ : "r5", "m0", "memory")
+
+#define BREV_STORE_bnew(ADDR, VAL, INC) \
+ BREV_STORE_NEW(b, ADDR, VAL, INC)
+#define BREV_STORE_hnew(ADDR, VAL, INC) \
+ BREV_STORE_NEW(h, ADDR, VAL, INC)
+#define BREV_STORE_wnew(ADDR, VAL, INC) \
+ BREV_STORE_NEW(w, ADDR, VAL, INC)
+
+int bitreverse(int x)
+{
+ int result = 0;
+ int i;
+ for (i = 0; i < NBITS; i++) {
+ result <<= 1;
+ result |= x & 1;
+ x >>= 1;
+ }
+ return result;
+}
+
+int sext8(int x)
+{
+ return (x << 24) >> 24;
+}
+
+void check(int i, long long result, long long expect)
+{
+ if (result != expect) {
+ printf("ERROR(%d): 0x%04llx != 0x%04llx\n", i, result, expect);
+ err++;
+ }
+}
+
+#define TEST_BREV_LOAD(SZ, TYPE, BUF, SHIFT, EXP) \
+ do { \
+ p = BUF; \
+ for (i = 0; i < SIZE; i++) { \
+ TYPE result; \
+ BREV_LOAD_##SZ(result, p, 1 << (SHIFT - NBITS)); \
+ check(i, result, EXP); \
+ } \
+ } while (0)
+
+#define TEST_BREV_STORE(SZ, TYPE, BUF, VAL, SHIFT) \
+ do { \
+ p = BUF; \
+ memset(BUF, 0xff, sizeof(BUF)); \
+ for (i = 0; i < SIZE; i++) { \
+ BREV_STORE_##SZ(p, (TYPE)(VAL), 1 << (SHIFT - NBITS)); \
+ } \
+ for (i = 0; i < SIZE; i++) { \
+ check(i, BUF[i], bitreverse(i)); \
+ } \
+ } while (0)
+
+#define TEST_BREV_STORE_NEW(SZ, BUF, SHIFT) \
+ do { \
+ p = BUF; \
+ memset(BUF, 0xff, sizeof(BUF)); \
+ for (i = 0; i < SIZE; i++) { \
+ BREV_STORE_##SZ(p, i, 1 << (SHIFT - NBITS)); \
+ } \
+ for (i = 0; i < SIZE; i++) { \
+ check(i, BUF[i], bitreverse(i)); \
+ } \
+ } while (0)
+
+/*
+ * We'll set high_half[i] = i << 16 for use in the .H form of store
+ * which stores from the high half of the word.
+ */
+int high_half[SIZE];
+
+int main()
+{
+ void *p;
+ int i;
+
+ for (i = 0; i < SIZE; i++) {
+ bbuf[i] = bitreverse(i);
+ hbuf[i] = bitreverse(i);
+ wbuf[i] = bitreverse(i);
+ dbuf[i] = bitreverse(i);
+ high_half[i] = i << 16;
+ }
+
+ TEST_BREV_LOAD(b, int, bbuf, 16, sext8(i));
+ TEST_BREV_LOAD(ub, int, bbuf, 16, i);
+ TEST_BREV_LOAD(h, int, hbuf, 15, i);
+ TEST_BREV_LOAD(uh, int, hbuf, 15, i);
+ TEST_BREV_LOAD(w, int, wbuf, 14, i);
+ TEST_BREV_LOAD(d, long long, dbuf, 13, i);
+
+ TEST_BREV_STORE(b, int, bbuf, i, 16);
+ TEST_BREV_STORE(h, int, hbuf, i, 15);
+ TEST_BREV_STORE(f, int, hbuf, high_half[i], 15);
+ TEST_BREV_STORE(w, int, wbuf, i, 14);
+ TEST_BREV_STORE(d, long long, dbuf, i, 13);
+
+ TEST_BREV_STORE_NEW(bnew, bbuf, 16);
+ TEST_BREV_STORE_NEW(hnew, hbuf, 15);
+ TEST_BREV_STORE_NEW(wnew, wbuf, 14);
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/circ.c b/tests/tcg/hexagon/circ.c
new file mode 100644
index 0000000000..67a1aa3054
--- /dev/null
+++ b/tests/tcg/hexagon/circ.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+
+#define DEBUG 0
+#define DEBUG_PRINTF(...) \
+ do { \
+ if (DEBUG) { \
+ printf(__VA_ARGS__); \
+ } \
+ } while (0)
+
+
+#define NBYTES (1 << 8)
+#define NHALFS (NBYTES / sizeof(short))
+#define NWORDS (NBYTES / sizeof(int))
+#define NDOBLS (NBYTES / sizeof(long long))
+
+long long dbuf[NDOBLS] __attribute__((aligned(1 << 12))) = {0};
+int wbuf[NWORDS] __attribute__((aligned(1 << 12))) = {0};
+short hbuf[NHALFS] __attribute__((aligned(1 << 12))) = {0};
+unsigned char bbuf[NBYTES] __attribute__((aligned(1 << 12))) = {0};
+
+/*
+ * We use the C preporcessor to deal with the combinations of types
+ */
+
+#define INIT(BUF, N) \
+ void init_##BUF(void) \
+ { \
+ int i; \
+ for (i = 0; i < N; i++) { \
+ BUF[i] = i; \
+ } \
+ } \
+
+INIT(bbuf, NBYTES)
+INIT(hbuf, NHALFS)
+INIT(wbuf, NWORDS)
+INIT(dbuf, NDOBLS)
+
+/*
+ * Macros for performing circular load
+ * RES result
+ * ADDR address
+ * START start address of buffer
+ * LEN length of buffer (in bytes)
+ * INC address increment (in bytes for IMM, elements for REG)
+ */
+#define CIRC_LOAD_IMM(SIZE, RES, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %3\n\t" \
+ "m0 = r4\n\t" \
+ "cs0 = %2\n\t" \
+ "%0 = mem" #SIZE "(%1++#" #INC ":circ(M0))\n\t" \
+ : "=r"(RES), "+r"(ADDR) \
+ : "r"(START), "r"(LEN) \
+ : "r4", "m0", "cs0")
+#define CIRC_LOAD_IMM_b(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(b, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_IMM_ub(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(ub, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_IMM_h(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(h, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_IMM_uh(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(uh, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_IMM_w(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(w, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_IMM_d(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_IMM(d, RES, ADDR, START, LEN, INC)
+
+/*
+ * The mreg has the following pieces
+ * mreg[31:28] increment[10:7]
+ * mreg[27:24] K value (used Hexagon v3 and earlier)
+ * mreg[23:17] increment[6:0]
+ * mreg[16:0] circular buffer length
+ */
+static int build_mreg(int inc, int K, int len)
+{
+ return ((inc & 0x780) << 21) |
+ ((K & 0xf) << 24) |
+ ((inc & 0x7f) << 17) |
+ (len & 0x1ffff);
+}
+
+#define CIRC_LOAD_REG(SIZE, RES, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %2\n\t" \
+ "m1 = r4\n\t" \
+ "cs1 = %3\n\t" \
+ "%0 = mem" #SIZE "(%1++I:circ(M1))\n\t" \
+ : "=r"(RES), "+r"(ADDR) \
+ : "r"(build_mreg((INC), 0, (LEN))), \
+ "r"(START) \
+ : "r4", "m1", "cs1")
+#define CIRC_LOAD_REG_b(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(b, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_REG_ub(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(ub, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_REG_h(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(h, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_REG_uh(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(uh, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_REG_w(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(w, RES, ADDR, START, LEN, INC)
+#define CIRC_LOAD_REG_d(RES, ADDR, START, LEN, INC) \
+ CIRC_LOAD_REG(d, RES, ADDR, START, LEN, INC)
+
+/*
+ * Macros for performing circular store
+ * VAL value to store
+ * ADDR address
+ * START start address of buffer
+ * LEN length of buffer (in bytes)
+ * INC address increment (in bytes for IMM, elements for REG)
+ */
+#define CIRC_STORE_IMM(SIZE, PART, VAL, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %3\n\t" \
+ "m0 = r4\n\t" \
+ "cs0 = %1\n\t" \
+ "mem" #SIZE "(%0++#" #INC ":circ(M0)) = %2" PART "\n\t" \
+ : "+r"(ADDR) \
+ : "r"(START), "r"(VAL), "r"(LEN) \
+ : "r4", "m0", "cs0", "memory")
+#define CIRC_STORE_IMM_b(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_IMM(b, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_h(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_IMM(h, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_f(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_IMM(h, ".H", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_w(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_IMM(w, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_d(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_IMM(d, "", VAL, ADDR, START, LEN, INC)
+
+#define CIRC_STORE_NEW_IMM(SIZE, VAL, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %3\n\t" \
+ "m0 = r4\n\t" \
+ "cs0 = %1\n\t" \
+ "{\n\t" \
+ " r5 = %2\n\t" \
+ " mem" #SIZE "(%0++#" #INC ":circ(M0)) = r5.new\n\t" \
+ "}\n\t" \
+ : "+r"(ADDR) \
+ : "r"(START), "r"(VAL), "r"(LEN) \
+ : "r4", "r5", "m0", "cs0", "memory")
+#define CIRC_STORE_IMM_bnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_IMM(b, VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_hnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_IMM(h, VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_IMM_wnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_IMM(w, VAL, ADDR, START, LEN, INC)
+
+#define CIRC_STORE_REG(SIZE, PART, VAL, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %1\n\t" \
+ "m1 = r4\n\t" \
+ "cs1 = %2\n\t" \
+ "mem" #SIZE "(%0++I:circ(M1)) = %3" PART "\n\t" \
+ : "+r"(ADDR) \
+ : "r"(build_mreg((INC), 0, (LEN))), \
+ "r"(START), \
+ "r"(VAL) \
+ : "r4", "m1", "cs1", "memory")
+#define CIRC_STORE_REG_b(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_REG(b, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_h(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_REG(h, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_f(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_REG(h, ".H", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_w(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_REG(w, "", VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_d(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_REG(d, "", VAL, ADDR, START, LEN, INC)
+
+#define CIRC_STORE_NEW_REG(SIZE, VAL, ADDR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %1\n\t" \
+ "m1 = r4\n\t" \
+ "cs1 = %2\n\t" \
+ "{\n\t" \
+ " r5 = %3\n\t" \
+ " mem" #SIZE "(%0++I:circ(M1)) = r5.new\n\t" \
+ "}\n\t" \
+ : "+r"(ADDR) \
+ : "r"(build_mreg((INC), 0, (LEN))), \
+ "r"(START), \
+ "r"(VAL) \
+ : "r4", "r5", "m1", "cs1", "memory")
+#define CIRC_STORE_REG_bnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_REG(b, VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_hnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_REG(h, VAL, ADDR, START, LEN, INC)
+#define CIRC_STORE_REG_wnew(VAL, ADDR, START, LEN, INC) \
+ CIRC_STORE_NEW_REG(w, VAL, ADDR, START, LEN, INC)
+
+
+int err;
+
+/* We'll test increments +1 and -1 */
+void check_load(int i, long long result, int inc, int size)
+{
+ int expect = (i * inc);
+ while (expect >= size) {
+ expect -= size;
+ }
+ while (expect < 0) {
+ expect += size;
+ }
+ if (result != expect) {
+ printf("ERROR(%d): %lld != %d\n", i, result, expect);
+ err++;
+ }
+}
+
+#define TEST_LOAD_IMM(SZ, TYPE, BUF, BUFSIZE, INC, FMT) \
+void circ_test_load_imm_##SZ(void) \
+{ \
+ TYPE *p = (TYPE *)BUF; \
+ int size = 10; \
+ int i; \
+ for (i = 0; i < BUFSIZE; i++) { \
+ TYPE element; \
+ CIRC_LOAD_IMM_##SZ(element, p, BUF, size * sizeof(TYPE), (INC)); \
+ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \
+ i, p, element); \
+ check_load(i, element, ((INC) / (int)sizeof(TYPE)), size); \
+ } \
+ p = (TYPE *)BUF; \
+ for (i = 0; i < BUFSIZE; i++) { \
+ TYPE element; \
+ CIRC_LOAD_IMM_##SZ(element, p, BUF, size * sizeof(TYPE), -(INC)); \
+ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \
+ i, p, element); \
+ check_load(i, element, (-(INC) / (int)sizeof(TYPE)), size); \
+ } \
+}
+
+TEST_LOAD_IMM(b, char, bbuf, NBYTES, 1, d)
+TEST_LOAD_IMM(ub, unsigned char, bbuf, NBYTES, 1, d)
+TEST_LOAD_IMM(h, short, hbuf, NHALFS, 2, d)
+TEST_LOAD_IMM(uh, unsigned short, hbuf, NHALFS, 2, d)
+TEST_LOAD_IMM(w, int, wbuf, NWORDS, 4, d)
+TEST_LOAD_IMM(d, long long, dbuf, NDOBLS, 8, lld)
+
+#define TEST_LOAD_REG(SZ, TYPE, BUF, BUFSIZE, FMT) \
+void circ_test_load_reg_##SZ(void) \
+{ \
+ TYPE *p = (TYPE *)BUF; \
+ int size = 13; \
+ int i; \
+ for (i = 0; i < BUFSIZE; i++) { \
+ TYPE element; \
+ CIRC_LOAD_REG_##SZ(element, p, BUF, size * sizeof(TYPE), 1); \
+ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \
+ i, p, element); \
+ check_load(i, element, 1, size); \
+ } \
+ p = (TYPE *)BUF; \
+ for (i = 0; i < BUFSIZE; i++) { \
+ TYPE element; \
+ CIRC_LOAD_REG_##SZ(element, p, BUF, size * sizeof(TYPE), -1); \
+ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2" #FMT "\n", \
+ i, p, element); \
+ check_load(i, element, -1, size); \
+ } \
+}
+
+TEST_LOAD_REG(b, char, bbuf, NBYTES, d)
+TEST_LOAD_REG(ub, unsigned char, bbuf, NBYTES, d)
+TEST_LOAD_REG(h, short, hbuf, NHALFS, d)
+TEST_LOAD_REG(uh, unsigned short, hbuf, NHALFS, d)
+TEST_LOAD_REG(w, int, wbuf, NWORDS, d)
+TEST_LOAD_REG(d, long long, dbuf, NDOBLS, lld)
+
+/* The circular stores will wrap around somewhere inside the buffer */
+#define CIRC_VAL(SZ, TYPE, BUFSIZE) \
+TYPE circ_val_##SZ(int i, int inc, int size) \
+{ \
+ int mod = BUFSIZE % size; \
+ int elem = i * inc; \
+ if (elem < 0) { \
+ if (-elem <= size - mod) { \
+ return (elem + BUFSIZE - mod); \
+ } else { \
+ return (elem + BUFSIZE + size - mod); \
+ } \
+ } else if (elem < mod) {\
+ return (elem + BUFSIZE - mod); \
+ } else { \
+ return (elem + BUFSIZE - size - mod); \
+ } \
+}
+
+CIRC_VAL(b, unsigned char, NBYTES)
+CIRC_VAL(h, short, NHALFS)
+CIRC_VAL(w, int, NWORDS)
+CIRC_VAL(d, long long, NDOBLS)
+
+/*
+ * Circular stores should only write to the first "size" elements of the buffer
+ * the remainder of the elements should have BUF[i] == i
+ */
+#define CHECK_STORE(SZ, BUF, BUFSIZE, FMT) \
+void check_store_##SZ(int inc, int size) \
+{ \
+ int i; \
+ for (i = 0; i < size; i++) { \
+ DEBUG_PRINTF(#BUF "[%3d] = 0x%02" #FMT ", guess = 0x%02" #FMT "\n", \
+ i, BUF[i], circ_val_##SZ(i, inc, size)); \
+ if (BUF[i] != circ_val_##SZ(i, inc, size)) { \
+ printf("ERROR(%3d): 0x%02" #FMT " != 0x%02" #FMT "\n", \
+ i, BUF[i], circ_val_##SZ(i, inc, size)); \
+ err++; \
+ } \
+ } \
+ for (i = size; i < BUFSIZE; i++) { \
+ if (BUF[i] != i) { \
+ printf("ERROR(%3d): 0x%02" #FMT " != 0x%02x\n", i, BUF[i], i); \
+ err++; \
+ } \
+ } \
+}
+
+CHECK_STORE(b, bbuf, NBYTES, x)
+CHECK_STORE(h, hbuf, NHALFS, x)
+CHECK_STORE(w, wbuf, NWORDS, x)
+CHECK_STORE(d, dbuf, NDOBLS, llx)
+
+#define CIRC_TEST_STORE_IMM(SZ, CHK, TYPE, BUF, BUFSIZE, SHIFT, INC) \
+void circ_test_store_imm_##SZ(void) \
+{ \
+ unsigned int size = 27; \
+ TYPE *p = BUF; \
+ TYPE val = 0; \
+ int i; \
+ init_##BUF(); \
+ for (i = 0; i < BUFSIZE; i++) { \
+ CIRC_STORE_IMM_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), INC); \
+ val++; \
+ } \
+ check_store_##CHK(((INC) / (int)sizeof(TYPE)), size); \
+ p = BUF; \
+ val = 0; \
+ init_##BUF(); \
+ for (i = 0; i < BUFSIZE; i++) { \
+ CIRC_STORE_IMM_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), \
+ -(INC)); \
+ val++; \
+ } \
+ check_store_##CHK((-(INC) / (int)sizeof(TYPE)), size); \
+}
+
+CIRC_TEST_STORE_IMM(b, b, unsigned char, bbuf, NBYTES, 0, 1)
+CIRC_TEST_STORE_IMM(h, h, short, hbuf, NHALFS, 0, 2)
+CIRC_TEST_STORE_IMM(f, h, short, hbuf, NHALFS, 16, 2)
+CIRC_TEST_STORE_IMM(w, w, int, wbuf, NWORDS, 0, 4)
+CIRC_TEST_STORE_IMM(d, d, long long, dbuf, NDOBLS, 0, 8)
+CIRC_TEST_STORE_IMM(bnew, b, unsigned char, bbuf, NBYTES, 0, 1)
+CIRC_TEST_STORE_IMM(hnew, h, short, hbuf, NHALFS, 0, 2)
+CIRC_TEST_STORE_IMM(wnew, w, int, wbuf, NWORDS, 0, 4)
+
+#define CIRC_TEST_STORE_REG(SZ, CHK, TYPE, BUF, BUFSIZE, SHIFT) \
+void circ_test_store_reg_##SZ(void) \
+{ \
+ TYPE *p = BUF; \
+ unsigned int size = 19; \
+ TYPE val = 0; \
+ int i; \
+ init_##BUF(); \
+ for (i = 0; i < BUFSIZE; i++) { \
+ CIRC_STORE_REG_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), 1); \
+ val++; \
+ } \
+ check_store_##CHK(1, size); \
+ p = BUF; \
+ val = 0; \
+ init_##BUF(); \
+ for (i = 0; i < BUFSIZE; i++) { \
+ CIRC_STORE_REG_##SZ(val << SHIFT, p, BUF, size * sizeof(TYPE), -1); \
+ val++; \
+ } \
+ check_store_##CHK(-1, size); \
+}
+
+CIRC_TEST_STORE_REG(b, b, unsigned char, bbuf, NBYTES, 0)
+CIRC_TEST_STORE_REG(h, h, short, hbuf, NHALFS, 0)
+CIRC_TEST_STORE_REG(f, h, short, hbuf, NHALFS, 16)
+CIRC_TEST_STORE_REG(w, w, int, wbuf, NWORDS, 0)
+CIRC_TEST_STORE_REG(d, d, long long, dbuf, NDOBLS, 0)
+CIRC_TEST_STORE_REG(bnew, b, unsigned char, bbuf, NBYTES, 0)
+CIRC_TEST_STORE_REG(hnew, h, short, hbuf, NHALFS, 0)
+CIRC_TEST_STORE_REG(wnew, w, int, wbuf, NWORDS, 0)
+
+/* Test the old scheme used in Hexagon V3 */
+static void circ_test_v3(void)
+{
+ int *p = wbuf;
+ int size = 15;
+ int K = 4; /* 64 bytes */
+ int element;
+ int i;
+
+ init_wbuf();
+
+ for (i = 0; i < NWORDS; i++) {
+ __asm__(
+ "r4 = %2\n\t"
+ "m1 = r4\n\t"
+ "%0 = memw(%1++I:circ(M1))\n\t"
+ : "=r"(element), "+r"(p)
+ : "r"(build_mreg(1, K, size * sizeof(int)))
+ : "r4", "m1");
+ DEBUG_PRINTF("i = %2d, p = 0x%p, element = %2d\n", i, p, element);
+ check_load(i, element, 1, size);
+ }
+}
+
+int main()
+{
+ init_bbuf();
+ init_hbuf();
+ init_wbuf();
+ init_dbuf();
+
+ DEBUG_PRINTF("NBYTES = %d\n", NBYTES);
+ DEBUG_PRINTF("Address of dbuf = 0x%p\n", dbuf);
+ DEBUG_PRINTF("Address of wbuf = 0x%p\n", wbuf);
+ DEBUG_PRINTF("Address of hbuf = 0x%p\n", hbuf);
+ DEBUG_PRINTF("Address of bbuf = 0x%p\n", bbuf);
+
+ circ_test_load_imm_b();
+ circ_test_load_imm_ub();
+ circ_test_load_imm_h();
+ circ_test_load_imm_uh();
+ circ_test_load_imm_w();
+ circ_test_load_imm_d();
+
+ circ_test_load_reg_b();
+ circ_test_load_reg_ub();
+ circ_test_load_reg_h();
+ circ_test_load_reg_uh();
+ circ_test_load_reg_w();
+ circ_test_load_reg_d();
+
+ circ_test_store_imm_b();
+ circ_test_store_imm_h();
+ circ_test_store_imm_f();
+ circ_test_store_imm_w();
+ circ_test_store_imm_d();
+ circ_test_store_imm_bnew();
+ circ_test_store_imm_hnew();
+ circ_test_store_imm_wnew();
+
+ circ_test_store_reg_b();
+ circ_test_store_reg_h();
+ circ_test_store_reg_f();
+ circ_test_store_reg_w();
+ circ_test_store_reg_d();
+ circ_test_store_reg_bnew();
+ circ_test_store_reg_hnew();
+ circ_test_store_reg_wnew();
+
+ circ_test_v3();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/fpstuff.c b/tests/tcg/hexagon/fpstuff.c
index e4f1a0eeb4..0dff429f4c 100644
--- a/tests/tcg/hexagon/fpstuff.c
+++ b/tests/tcg/hexagon/fpstuff.c
@@ -37,10 +37,12 @@ const int SF_NaN = 0x7fc00000;
const int SF_NaN_special = 0x7f800001;
const int SF_ANY = 0x3f800000;
const int SF_HEX_NAN = 0xffffffff;
+const int SF_small_neg = 0xab98fba8;
const long long DF_NaN = 0x7ff8000000000000ULL;
const long long DF_ANY = 0x3f80000000000000ULL;
const long long DF_HEX_NAN = 0xffffffffffffffffULL;
+const long long DF_small_neg = 0xbd731f7500000000ULL;
int err;
@@ -248,6 +250,87 @@ static void check_dfminmax(void)
check_fpstatus(usr, FPINVF);
}
+static void check_recip_exception(void)
+{
+ int result;
+ int usr;
+
+ /*
+ * Check that sfrecipa doesn't set status bits when
+ * a NaN with bit 22 non-zero is passed
+ */
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_NaN), "r"(SF_ANY)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, 0);
+
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_ANY), "r"(SF_NaN)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, 0);
+
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_NaN)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, 0);
+
+ /*
+ * Check that sfrecipa doesn't set status bits when
+ * a NaN with bit 22 zero is passed
+ */
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_NaN_special), "r"(SF_ANY)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, FPINVF);
+
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_ANY), "r"(SF_NaN_special)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, FPINVF);
+
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(SF_NaN_special)
+ : "r2", "p0", "usr");
+ check32(result, SF_HEX_NAN);
+ check_fpstatus(usr, FPINVF);
+
+ /*
+ * Check that sfrecipa properly sets divid-by-zero
+ */
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(0x885dc960), "r"(0x80000000)
+ : "r2", "p0", "usr");
+ check32(result, 0x3f800000);
+ check_fpstatus(usr, FPDBZF);
+
+ asm (CLEAR_FPSTATUS
+ "%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(result), "=r"(usr) : "r"(0x7f800000), "r"(SF_ZERO)
+ : "r2", "p0", "usr");
+ check32(result, 0x3f800000);
+ check_fpstatus(usr, 0);
+}
+
static void check_canonical_NaN(void)
{
int sf_result;
@@ -358,12 +441,171 @@ static void check_canonical_NaN(void)
check_fpstatus(usr, 0);
}
+static void check_invsqrta(void)
+{
+ int result;
+ int predval;
+
+ asm volatile("%0,p0 = sfinvsqrta(%2)\n\t"
+ "%1 = p0\n\t"
+ : "+r"(result), "=r"(predval)
+ : "r"(0x7f800000)
+ : "p0");
+ check32(result, 0xff800000);
+ check32(predval, 0x0);
+}
+
+static void check_float2int_convs()
+{
+ int res32;
+ long long res64;
+ int usr;
+
+ /*
+ * Check that the various forms of float-to-unsigned
+ * check sign before rounding
+ */
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2uw(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(SF_small_neg)
+ : "r2", "usr");
+ check32(res32, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2uw(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(SF_small_neg)
+ : "r2", "usr");
+ check32(res32, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2ud(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(SF_small_neg)
+ : "r2", "usr");
+ check64(res64, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2ud(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(SF_small_neg)
+ : "r2", "usr");
+ check64(res64, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2uw(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(DF_small_neg)
+ : "r2", "usr");
+ check32(res32, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2uw(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(DF_small_neg)
+ : "r2", "usr");
+ check32(res32, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2ud(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(DF_small_neg)
+ : "r2", "usr");
+ check64(res64, 0);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2ud(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(DF_small_neg)
+ : "r2", "usr");
+ check64(res64, 0);
+ check_fpstatus(usr, FPINVF);
+
+ /*
+ * Check that the various forms of float-to-signed return -1 for NaN
+ */
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2w(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(SF_NaN)
+ : "r2", "usr");
+ check32(res32, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2w(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(SF_NaN)
+ : "r2", "usr");
+ check32(res32, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2d(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(SF_NaN)
+ : "r2", "usr");
+ check64(res64, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_sf2d(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(SF_NaN)
+ : "r2", "usr");
+ check64(res64, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2w(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(DF_NaN)
+ : "r2", "usr");
+ check32(res32, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2w(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res32), "=r"(usr) : "r"(DF_NaN)
+ : "r2", "usr");
+ check32(res32, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2d(%2)\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(DF_NaN)
+ : "r2", "usr");
+ check64(res64, -1);
+ check_fpstatus(usr, FPINVF);
+
+ asm(CLEAR_FPSTATUS
+ "%0 = convert_df2d(%2):chop\n\t"
+ "%1 = usr\n\t"
+ : "=r"(res64), "=r"(usr) : "r"(DF_NaN)
+ : "r2", "usr");
+ check64(res64, -1);
+ check_fpstatus(usr, FPINVF);
+}
+
int main()
{
check_compare_exception();
check_sfminmax();
check_dfminmax();
+ check_recip_exception();
check_canonical_NaN();
+ check_invsqrta();
+ check_float2int_convs();
puts(err ? "FAIL" : "PASS");
return err ? 1 : 0;
diff --git a/tests/tcg/hexagon/load_align.c b/tests/tcg/hexagon/load_align.c
new file mode 100644
index 0000000000..12fc9cbd8f
--- /dev/null
+++ b/tests/tcg/hexagon/load_align.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * 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/>.
+ */
+
+/*
+ * Test load align instructions
+ *
+ * Example
+ * r1:0 = memh_fifo(r1+#0)
+ * loads a half word from memory, shifts the destination register
+ * right by one half word and inserts the loaded value into the high
+ * half word of the destination.
+ *
+ * There are 8 addressing modes and byte and half word variants, for a
+ * total of 16 instructions to test
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+int err;
+
+char buf[16] __attribute__((aligned(1 << 16)));
+
+void init_buf(void)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ buf[i] = i + 1;
+ }
+}
+
+void __check(int line, long long result, long long expect)
+{
+ if (result != expect) {
+ printf("ERROR at line %d: 0x%016llx != 0x%016llx\n",
+ line, result, expect);
+ err++;
+ }
+}
+
+#define check(RES, EXP) __check(__LINE__, RES, EXP)
+
+void __checkp(int line, void *p, void *expect)
+{
+ if (p != expect) {
+ printf("ERROR at line %d: 0x%p != 0x%p\n", line, p, expect);
+ err++;
+ }
+}
+
+#define checkp(RES, EXP) __checkp(__LINE__, RES, EXP)
+
+/*
+ ****************************************************************************
+ * _io addressing mode (addr + offset)
+ */
+#define LOAD_io(SZ, RES, ADDR, OFF) \
+ __asm__( \
+ "%0 = mem" #SZ "_fifo(%1+#" #OFF ")\n\t" \
+ : "+r"(RES) \
+ : "r"(ADDR))
+#define LOAD_io_b(RES, ADDR, OFF) \
+ LOAD_io(b, RES, ADDR, OFF)
+#define LOAD_io_h(RES, ADDR, OFF) \
+ LOAD_io(h, RES, ADDR, OFF)
+
+#define TEST_io(NAME, SZ, SIZE, EXP1, EXP2, EXP3, EXP4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ LOAD_io_##SZ(result, buf, 0 * (SIZE)); \
+ check(result, (EXP1)); \
+ LOAD_io_##SZ(result, buf, 1 * (SIZE)); \
+ check(result, (EXP2)); \
+ LOAD_io_##SZ(result, buf, 2 * (SIZE)); \
+ check(result, (EXP3)); \
+ LOAD_io_##SZ(result, buf, 3 * (SIZE)); \
+ check(result, (EXP4)); \
+}
+
+TEST_io(loadalignb_io, b, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x030201ffffffffffLL, 0x04030201ffffffffLL)
+TEST_io(loadalignh_io, h, 2,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x060504030201ffffLL, 0x0807060504030201LL)
+
+/*
+ ****************************************************************************
+ * _ur addressing mode (index << offset + base)
+ */
+#define LOAD_ur(SZ, RES, SHIFT, IDX) \
+ __asm__( \
+ "%0 = mem" #SZ "_fifo(%1<<#" #SHIFT " + ##buf)\n\t" \
+ : "+r"(RES) \
+ : "r"(IDX))
+#define LOAD_ur_b(RES, SHIFT, IDX) \
+ LOAD_ur(b, RES, SHIFT, IDX)
+#define LOAD_ur_h(RES, SHIFT, IDX) \
+ LOAD_ur(h, RES, SHIFT, IDX)
+
+#define TEST_ur(NAME, SZ, SHIFT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ LOAD_ur_##SZ(result, (SHIFT), 0); \
+ check(result, (RES1)); \
+ LOAD_ur_##SZ(result, (SHIFT), 1); \
+ check(result, (RES2)); \
+ LOAD_ur_##SZ(result, (SHIFT), 2); \
+ check(result, (RES3)); \
+ LOAD_ur_##SZ(result, (SHIFT), 3); \
+ check(result, (RES4)); \
+}
+
+TEST_ur(loadalignb_ur, b, 1,
+ 0x01ffffffffffffffLL, 0x0301ffffffffffffLL,
+ 0x050301ffffffffffLL, 0x07050301ffffffffLL)
+TEST_ur(loadalignh_ur, h, 1,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x060504030201ffffLL, 0x0807060504030201LL)
+
+/*
+ ****************************************************************************
+ * _ap addressing mode (addr = base)
+ */
+#define LOAD_ap(SZ, RES, PTR, ADDR) \
+ __asm__( \
+ "%0 = mem" #SZ "_fifo(%1 = ##" #ADDR ")\n\t" \
+ : "+r"(RES), "=r"(PTR))
+#define LOAD_ap_b(RES, PTR, ADDR) \
+ LOAD_ap(b, RES, PTR, ADDR)
+#define LOAD_ap_h(RES, PTR, ADDR) \
+ LOAD_ap(h, RES, PTR, ADDR)
+
+#define TEST_ap(NAME, SZ, SIZE, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr; \
+ LOAD_ap_##SZ(result, ptr, (buf + 0 * (SIZE))); \
+ check(result, (RES1)); \
+ checkp(ptr, &buf[0 * (SIZE)]); \
+ LOAD_ap_##SZ(result, ptr, (buf + 1 * (SIZE))); \
+ check(result, (RES2)); \
+ checkp(ptr, &buf[1 * (SIZE)]); \
+ LOAD_ap_##SZ(result, ptr, (buf + 2 * (SIZE))); \
+ check(result, (RES3)); \
+ checkp(ptr, &buf[2 * (SIZE)]); \
+ LOAD_ap_##SZ(result, ptr, (buf + 3 * (SIZE))); \
+ check(result, (RES4)); \
+ checkp(ptr, &buf[3 * (SIZE)]); \
+}
+
+TEST_ap(loadalignb_ap, b, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x030201ffffffffffLL, 0x04030201ffffffffLL)
+TEST_ap(loadalignh_ap, h, 2,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x060504030201ffffLL, 0x0807060504030201LL)
+
+/*
+ ****************************************************************************
+ * _rp addressing mode (addr ++ modifer-reg)
+ */
+#define LOAD_pr(SZ, RES, PTR, INC) \
+ __asm__( \
+ "m0 = %2\n\t" \
+ "%0 = mem" #SZ "_fifo(%1++m0)\n\t" \
+ : "+r"(RES), "+r"(PTR) \
+ : "r"(INC) \
+ : "m0")
+#define LOAD_pr_b(RES, PTR, INC) \
+ LOAD_pr(b, RES, PTR, INC)
+#define LOAD_pr_h(RES, PTR, INC) \
+ LOAD_pr(h, RES, PTR, INC)
+
+#define TEST_pr(NAME, SZ, SIZE, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr = buf; \
+ LOAD_pr_##SZ(result, ptr, (SIZE)); \
+ check(result, (RES1)); \
+ checkp(ptr, &buf[1 * (SIZE)]); \
+ LOAD_pr_##SZ(result, ptr, (SIZE)); \
+ check(result, (RES2)); \
+ checkp(ptr, &buf[2 * (SIZE)]); \
+ LOAD_pr_##SZ(result, ptr, (SIZE)); \
+ check(result, (RES3)); \
+ checkp(ptr, &buf[3 * (SIZE)]); \
+ LOAD_pr_##SZ(result, ptr, (SIZE)); \
+ check(result, (RES4)); \
+ checkp(ptr, &buf[4 * (SIZE)]); \
+}
+
+TEST_pr(loadalignb_pr, b, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x030201ffffffffffLL, 0x04030201ffffffffLL)
+TEST_pr(loadalignh_pr, h, 2,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x060504030201ffffLL, 0x0807060504030201LL)
+
+/*
+ ****************************************************************************
+ * _pbr addressing mode (addr ++ modifer-reg:brev)
+ */
+#define LOAD_pbr(SZ, RES, PTR) \
+ __asm__( \
+ "r4 = #(1 << (16 - 3))\n\t" \
+ "m0 = r4\n\t" \
+ "%0 = mem" #SZ "_fifo(%1++m0:brev)\n\t" \
+ : "+r"(RES), "+r"(PTR) \
+ : \
+ : "r4", "m0")
+#define LOAD_pbr_b(RES, PTR) \
+ LOAD_pbr(b, RES, PTR)
+#define LOAD_pbr_h(RES, PTR) \
+ LOAD_pbr(h, RES, PTR)
+
+#define TEST_pbr(NAME, SZ, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr = buf; \
+ LOAD_pbr_##SZ(result, ptr); \
+ check(result, (RES1)); \
+ LOAD_pbr_##SZ(result, ptr); \
+ check(result, (RES2)); \
+ LOAD_pbr_##SZ(result, ptr); \
+ check(result, (RES3)); \
+ LOAD_pbr_##SZ(result, ptr); \
+ check(result, (RES4)); \
+}
+
+TEST_pbr(loadalignb_pbr, b,
+ 0x01ffffffffffffffLL, 0x0501ffffffffffffLL,
+ 0x030501ffffffffffLL, 0x07030501ffffffffLL)
+TEST_pbr(loadalignh_pbr, h,
+ 0x0201ffffffffffffLL, 0x06050201ffffffffLL,
+ 0x040306050201ffffLL, 0x0807040306050201LL)
+
+/*
+ ****************************************************************************
+ * _pi addressing mode (addr ++ inc)
+ */
+#define LOAD_pi(SZ, RES, PTR, INC) \
+ __asm__( \
+ "%0 = mem" #SZ "_fifo(%1++#" #INC ")\n\t" \
+ : "+r"(RES), "+r"(PTR))
+#define LOAD_pi_b(RES, PTR, INC) \
+ LOAD_pi(b, RES, PTR, INC)
+#define LOAD_pi_h(RES, PTR, INC) \
+ LOAD_pi(h, RES, PTR, INC)
+
+#define TEST_pi(NAME, SZ, INC, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr = buf; \
+ LOAD_pi_##SZ(result, ptr, (INC)); \
+ check(result, (RES1)); \
+ checkp(ptr, &buf[1 * (INC)]); \
+ LOAD_pi_##SZ(result, ptr, (INC)); \
+ check(result, (RES2)); \
+ checkp(ptr, &buf[2 * (INC)]); \
+ LOAD_pi_##SZ(result, ptr, (INC)); \
+ check(result, (RES3)); \
+ checkp(ptr, &buf[3 * (INC)]); \
+ LOAD_pi_##SZ(result, ptr, (INC)); \
+ check(result, (RES4)); \
+ checkp(ptr, &buf[4 * (INC)]); \
+}
+
+TEST_pi(loadalignb_pi, b, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x030201ffffffffffLL, 0x04030201ffffffffLL)
+TEST_pi(loadalignh_pi, h, 2,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x060504030201ffffLL, 0x0807060504030201LL)
+
+/*
+ ****************************************************************************
+ * _pci addressing mode (addr ++ inc:circ)
+ */
+#define LOAD_pci(SZ, RES, PTR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %3\n\t" \
+ "m0 = r4\n\t" \
+ "cs0 = %2\n\t" \
+ "%0 = mem" #SZ "_fifo(%1++#" #INC ":circ(m0))\n\t" \
+ : "+r"(RES), "+r"(PTR) \
+ : "r"(START), "r"(LEN) \
+ : "r4", "m0", "cs0")
+#define LOAD_pci_b(RES, PTR, START, LEN, INC) \
+ LOAD_pci(b, RES, PTR, START, LEN, INC)
+#define LOAD_pci_h(RES, PTR, START, LEN, INC) \
+ LOAD_pci(h, RES, PTR, START, LEN, INC)
+
+#define TEST_pci(NAME, SZ, LEN, INC, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr = buf; \
+ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES1)); \
+ checkp(ptr, &buf[(1 * (INC)) % (LEN)]); \
+ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES2)); \
+ checkp(ptr, &buf[(2 * (INC)) % (LEN)]); \
+ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES3)); \
+ checkp(ptr, &buf[(3 * (INC)) % (LEN)]); \
+ LOAD_pci_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES4)); \
+ checkp(ptr, &buf[(4 * (INC)) % (LEN)]); \
+}
+
+TEST_pci(loadalignb_pci, b, 2, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x010201ffffffffffLL, 0x02010201ffffffffLL)
+TEST_pci(loadalignh_pci, h, 4, 2,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x020104030201ffffLL, 0x0403020104030201LL)
+
+/*
+ ****************************************************************************
+ * _pcr addressing mode (addr ++ I:circ(modifier-reg))
+ */
+#define LOAD_pcr(SZ, RES, PTR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %2\n\t" \
+ "m1 = r4\n\t" \
+ "cs1 = %3\n\t" \
+ "%0 = mem" #SZ "_fifo(%1++I:circ(m1))\n\t" \
+ : "+r"(RES), "+r"(PTR) \
+ : "r"((((INC) & 0x7f) << 17) | ((LEN) & 0x1ffff)), \
+ "r"(START) \
+ : "r4", "m1", "cs1")
+#define LOAD_pcr_b(RES, PTR, START, LEN, INC) \
+ LOAD_pcr(b, RES, PTR, START, LEN, INC)
+#define LOAD_pcr_h(RES, PTR, START, LEN, INC) \
+ LOAD_pcr(h, RES, PTR, START, LEN, INC)
+
+#define TEST_pcr(NAME, SZ, SIZE, LEN, INC, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ long long result = ~0LL; \
+ void *ptr = buf; \
+ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES1)); \
+ checkp(ptr, &buf[(1 * (INC) * (SIZE)) % (LEN)]); \
+ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES2)); \
+ checkp(ptr, &buf[(2 * (INC) * (SIZE)) % (LEN)]); \
+ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES3)); \
+ checkp(ptr, &buf[(3 * (INC) * (SIZE)) % (LEN)]); \
+ LOAD_pcr_##SZ(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES4)); \
+ checkp(ptr, &buf[(4 * (INC) * (SIZE)) % (LEN)]); \
+}
+
+TEST_pcr(loadalignb_pcr, b, 1, 2, 1,
+ 0x01ffffffffffffffLL, 0x0201ffffffffffffLL,
+ 0x010201ffffffffffLL, 0x02010201ffffffffLL)
+TEST_pcr(loadalignh_pcr, h, 2, 4, 1,
+ 0x0201ffffffffffffLL, 0x04030201ffffffffLL,
+ 0x020104030201ffffLL, 0x0403020104030201LL)
+
+int main()
+{
+ init_buf();
+
+ test_loadalignb_io();
+ test_loadalignh_io();
+
+ test_loadalignb_ur();
+ test_loadalignh_ur();
+
+ test_loadalignb_ap();
+ test_loadalignh_ap();
+
+ test_loadalignb_pr();
+ test_loadalignh_pr();
+
+ test_loadalignb_pbr();
+ test_loadalignh_pbr();
+
+ test_loadalignb_pi();
+ test_loadalignh_pi();
+
+ test_loadalignb_pci();
+ test_loadalignh_pci();
+
+ test_loadalignb_pcr();
+ test_loadalignh_pcr();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/load_unpack.c b/tests/tcg/hexagon/load_unpack.c
new file mode 100644
index 0000000000..3575a37a28
--- /dev/null
+++ b/tests/tcg/hexagon/load_unpack.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * 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/>.
+ */
+
+/*
+ * Test load unpack instructions
+ *
+ * Example
+ * r0 = memubh(r1+#0)
+ * loads a half word from memory and zero-extends the 2 bytes to form a word
+ *
+ * For each addressing mode, there are 4 tests
+ * bzw2 unsigned 2 elements
+ * bsw2 signed 2 elements
+ * bzw4 unsigned 4 elements
+ * bsw4 signed 4 elements
+ * There are 8 addressing modes, for a total of 32 instructions to test
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+int err;
+
+char buf[16] __attribute__((aligned(1 << 16)));
+
+void init_buf(void)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ int sign = i % 2 == 0 ? 0x80 : 0;
+ buf[i] = sign | (i + 1);
+ }
+}
+
+void __check(int line, long long result, long long expect)
+{
+ if (result != expect) {
+ printf("ERROR at line %d: 0x%08llx != 0x%08llx\n",
+ line, result, expect);
+ err++;
+ }
+}
+
+#define check(RES, EXP) __check(__LINE__, RES, EXP)
+
+void __checkp(int line, void *p, void *expect)
+{
+ if (p != expect) {
+ printf("ERROR at line %d: 0x%p != 0x%p\n", line, p, expect);
+ err++;
+ }
+}
+
+#define checkp(RES, EXP) __checkp(__LINE__, RES, EXP)
+
+/*
+ ****************************************************************************
+ * _io addressing mode (addr + offset)
+ */
+#define BxW_LOAD_io(SZ, RES, ADDR, OFF) \
+ __asm__( \
+ "%0 = mem" #SZ "(%1+#" #OFF ")\n\t" \
+ : "=r"(RES) \
+ : "r"(ADDR))
+#define BxW_LOAD_io_Z(RES, ADDR, OFF) \
+ BxW_LOAD_io(ubh, RES, ADDR, OFF)
+#define BxW_LOAD_io_S(RES, ADDR, OFF) \
+ BxW_LOAD_io(bh, RES, ADDR, OFF)
+
+#define TEST_io(NAME, TYPE, SIGN, SIZE, EXT, EXP1, EXP2, EXP3, EXP4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ init_buf(); \
+ BxW_LOAD_io_##SIGN(result, buf, 0 * (SIZE)); \
+ check(result, (EXP1) | (EXT)); \
+ BxW_LOAD_io_##SIGN(result, buf, 1 * (SIZE)); \
+ check(result, (EXP2) | (EXT)); \
+ BxW_LOAD_io_##SIGN(result, buf, 2 * (SIZE)); \
+ check(result, (EXP3) | (EXT)); \
+ BxW_LOAD_io_##SIGN(result, buf, 3 * (SIZE)); \
+ check(result, (EXP4) | (EXT)); \
+}
+
+
+TEST_io(loadbzw2_io, int, Z, 2, 0x00000000,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_io(loadbsw2_io, int, S, 2, 0x0000ff00,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_io(loadbzw4_io, long long, Z, 4, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+TEST_io(loadbsw4_io, long long, S, 4, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+
+/*
+ ****************************************************************************
+ * _ur addressing mode (index << offset + base)
+ */
+#define BxW_LOAD_ur(SZ, RES, SHIFT, IDX) \
+ __asm__( \
+ "%0 = mem" #SZ "(%1<<#" #SHIFT " + ##buf)\n\t" \
+ : "=r"(RES) \
+ : "r"(IDX))
+#define BxW_LOAD_ur_Z(RES, SHIFT, IDX) \
+ BxW_LOAD_ur(ubh, RES, SHIFT, IDX)
+#define BxW_LOAD_ur_S(RES, SHIFT, IDX) \
+ BxW_LOAD_ur(bh, RES, SHIFT, IDX)
+
+#define TEST_ur(NAME, TYPE, SIGN, SHIFT, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ init_buf(); \
+ BxW_LOAD_ur_##SIGN(result, (SHIFT), 0); \
+ check(result, (RES1) | (EXT)); \
+ BxW_LOAD_ur_##SIGN(result, (SHIFT), 1); \
+ check(result, (RES2) | (EXT)); \
+ BxW_LOAD_ur_##SIGN(result, (SHIFT), 2); \
+ check(result, (RES3) | (EXT)); \
+ BxW_LOAD_ur_##SIGN(result, (SHIFT), 3); \
+ check(result, (RES4) | (EXT)); \
+} \
+
+TEST_ur(loadbzw2_ur, int, Z, 1, 0x00000000,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_ur(loadbsw2_ur, int, S, 1, 0x0000ff00,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_ur(loadbzw4_ur, long long, Z, 2, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+TEST_ur(loadbsw4_ur, long long, S, 2, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+
+/*
+ ****************************************************************************
+ * _ap addressing mode (addr = base)
+ */
+#define BxW_LOAD_ap(SZ, RES, PTR, ADDR) \
+ __asm__( \
+ "%0 = mem" #SZ "(%1 = ##" #ADDR ")\n\t" \
+ : "=r"(RES), "=r"(PTR))
+#define BxW_LOAD_ap_Z(RES, PTR, ADDR) \
+ BxW_LOAD_ap(ubh, RES, PTR, ADDR)
+#define BxW_LOAD_ap_S(RES, PTR, ADDR) \
+ BxW_LOAD_ap(bh, RES, PTR, ADDR)
+
+#define TEST_ap(NAME, TYPE, SIGN, SIZE, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr; \
+ init_buf(); \
+ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 0 * (SIZE))); \
+ check(result, (RES1) | (EXT)); \
+ checkp(ptr, &buf[0 * (SIZE)]); \
+ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 1 * (SIZE))); \
+ check(result, (RES2) | (EXT)); \
+ checkp(ptr, &buf[1 * (SIZE)]); \
+ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 2 * (SIZE))); \
+ check(result, (RES3) | (EXT)); \
+ checkp(ptr, &buf[2 * (SIZE)]); \
+ BxW_LOAD_ap_##SIGN(result, ptr, (buf + 3 * (SIZE))); \
+ check(result, (RES4) | (EXT)); \
+ checkp(ptr, &buf[3 * (SIZE)]); \
+}
+
+TEST_ap(loadbzw2_ap, int, Z, 2, 0x00000000,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_ap(loadbsw2_ap, int, S, 2, 0x0000ff00,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_ap(loadbzw4_ap, long long, Z, 4, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+TEST_ap(loadbsw4_ap, long long, S, 4, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+
+/*
+ ****************************************************************************
+ * _rp addressing mode (addr ++ modifer-reg)
+ */
+#define BxW_LOAD_pr(SZ, RES, PTR, INC) \
+ __asm__( \
+ "m0 = %2\n\t" \
+ "%0 = mem" #SZ "(%1++m0)\n\t" \
+ : "=r"(RES), "+r"(PTR) \
+ : "r"(INC) \
+ : "m0")
+#define BxW_LOAD_pr_Z(RES, PTR, INC) \
+ BxW_LOAD_pr(ubh, RES, PTR, INC)
+#define BxW_LOAD_pr_S(RES, PTR, INC) \
+ BxW_LOAD_pr(bh, RES, PTR, INC)
+
+#define TEST_pr(NAME, TYPE, SIGN, SIZE, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr = buf; \
+ init_buf(); \
+ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \
+ check(result, (RES1) | (EXT)); \
+ checkp(ptr, &buf[1 * (SIZE)]); \
+ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \
+ check(result, (RES2) | (EXT)); \
+ checkp(ptr, &buf[2 * (SIZE)]); \
+ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \
+ check(result, (RES3) | (EXT)); \
+ checkp(ptr, &buf[3 * (SIZE)]); \
+ BxW_LOAD_pr_##SIGN(result, ptr, (SIZE)); \
+ check(result, (RES4) | (EXT)); \
+ checkp(ptr, &buf[4 * (SIZE)]); \
+}
+
+TEST_pr(loadbzw2_pr, int, Z, 2, 0x00000000,
+ 0x00020081, 0x0040083, 0x00060085, 0x00080087)
+TEST_pr(loadbsw2_pr, int, S, 2, 0x0000ff00,
+ 0x00020081, 0x0040083, 0x00060085, 0x00080087)
+TEST_pr(loadbzw4_pr, long long, Z, 4, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+TEST_pr(loadbsw4_pr, long long, S, 4, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+
+/*
+ ****************************************************************************
+ * _pbr addressing mode (addr ++ modifer-reg:brev)
+ */
+#define BxW_LOAD_pbr(SZ, RES, PTR) \
+ __asm__( \
+ "r4 = #(1 << (16 - 3))\n\t" \
+ "m0 = r4\n\t" \
+ "%0 = mem" #SZ "(%1++m0:brev)\n\t" \
+ : "=r"(RES), "+r"(PTR) \
+ : \
+ : "r4", "m0")
+#define BxW_LOAD_pbr_Z(RES, PTR) \
+ BxW_LOAD_pbr(ubh, RES, PTR)
+#define BxW_LOAD_pbr_S(RES, PTR) \
+ BxW_LOAD_pbr(bh, RES, PTR)
+
+#define TEST_pbr(NAME, TYPE, SIGN, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr = buf; \
+ init_buf(); \
+ BxW_LOAD_pbr_##SIGN(result, ptr); \
+ check(result, (RES1) | (EXT)); \
+ BxW_LOAD_pbr_##SIGN(result, ptr); \
+ check(result, (RES2) | (EXT)); \
+ BxW_LOAD_pbr_##SIGN(result, ptr); \
+ check(result, (RES3) | (EXT)); \
+ BxW_LOAD_pbr_##SIGN(result, ptr); \
+ check(result, (RES4) | (EXT)); \
+}
+
+TEST_pbr(loadbzw2_pbr, int, Z, 0x00000000,
+ 0x00020081, 0x00060085, 0x00040083, 0x00080087)
+TEST_pbr(loadbsw2_pbr, int, S, 0x0000ff00,
+ 0x00020081, 0x00060085, 0x00040083, 0x00080087)
+TEST_pbr(loadbzw4_pbr, long long, Z, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0006008500040083LL, 0x000a008900080087LL)
+TEST_pbr(loadbsw4_pbr, long long, S, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0006008500040083LL, 0x000a008900080087LL)
+
+/*
+ ****************************************************************************
+ * _pi addressing mode (addr ++ inc)
+ */
+#define BxW_LOAD_pi(SZ, RES, PTR, INC) \
+ __asm__( \
+ "%0 = mem" #SZ "(%1++#" #INC ")\n\t" \
+ : "=r"(RES), "+r"(PTR))
+#define BxW_LOAD_pi_Z(RES, PTR, INC) \
+ BxW_LOAD_pi(ubh, RES, PTR, INC)
+#define BxW_LOAD_pi_S(RES, PTR, INC) \
+ BxW_LOAD_pi(bh, RES, PTR, INC)
+
+#define TEST_pi(NAME, TYPE, SIGN, INC, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr = buf; \
+ init_buf(); \
+ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \
+ check(result, (RES1) | (EXT)); \
+ checkp(ptr, &buf[1 * (INC)]); \
+ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \
+ check(result, (RES2) | (EXT)); \
+ checkp(ptr, &buf[2 * (INC)]); \
+ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \
+ check(result, (RES3) | (EXT)); \
+ checkp(ptr, &buf[3 * (INC)]); \
+ BxW_LOAD_pi_##SIGN(result, ptr, (INC)); \
+ check(result, (RES4) | (EXT)); \
+ checkp(ptr, &buf[4 * (INC)]); \
+}
+
+TEST_pi(loadbzw2_pi, int, Z, 2, 0x00000000,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_pi(loadbsw2_pi, int, S, 2, 0x0000ff00,
+ 0x00020081, 0x00040083, 0x00060085, 0x00080087)
+TEST_pi(loadbzw4_pi, long long, Z, 4, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+TEST_pi(loadbsw4_pi, long long, S, 4, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x000c008b000a0089LL, 0x0010008f000e008dLL)
+
+/*
+ ****************************************************************************
+ * _pci addressing mode (addr ++ inc:circ)
+ */
+#define BxW_LOAD_pci(SZ, RES, PTR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %3\n\t" \
+ "m0 = r4\n\t" \
+ "cs0 = %2\n\t" \
+ "%0 = mem" #SZ "(%1++#" #INC ":circ(m0))\n\t" \
+ : "=r"(RES), "+r"(PTR) \
+ : "r"(START), "r"(LEN) \
+ : "r4", "m0", "cs0")
+#define BxW_LOAD_pci_Z(RES, PTR, START, LEN, INC) \
+ BxW_LOAD_pci(ubh, RES, PTR, START, LEN, INC)
+#define BxW_LOAD_pci_S(RES, PTR, START, LEN, INC) \
+ BxW_LOAD_pci(bh, RES, PTR, START, LEN, INC)
+
+#define TEST_pci(NAME, TYPE, SIGN, LEN, INC, EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr = buf; \
+ init_buf(); \
+ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES1) | (EXT)); \
+ checkp(ptr, &buf[(1 * (INC)) % (LEN)]); \
+ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES2) | (EXT)); \
+ checkp(ptr, &buf[(2 * (INC)) % (LEN)]); \
+ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES3) | (EXT)); \
+ checkp(ptr, &buf[(3 * (INC)) % (LEN)]); \
+ BxW_LOAD_pci_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES4) | (EXT)); \
+ checkp(ptr, &buf[(4 * (INC)) % (LEN)]); \
+}
+
+TEST_pci(loadbzw2_pci, int, Z, 6, 2, 0x00000000,
+ 0x00020081, 0x00040083, 0x00060085, 0x00020081)
+TEST_pci(loadbsw2_pci, int, S, 6, 2, 0x0000ff00,
+ 0x00020081, 0x00040083, 0x00060085, 0x00020081)
+TEST_pci(loadbzw4_pci, long long, Z, 8, 4, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0004008300020081LL, 0x0008008700060085LL)
+TEST_pci(loadbsw4_pci, long long, S, 8, 4, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0004008300020081LL, 0x0008008700060085LL)
+
+/*
+ ****************************************************************************
+ * _pcr addressing mode (addr ++ I:circ(modifier-reg))
+ */
+#define BxW_LOAD_pcr(SZ, RES, PTR, START, LEN, INC) \
+ __asm__( \
+ "r4 = %2\n\t" \
+ "m1 = r4\n\t" \
+ "cs1 = %3\n\t" \
+ "%0 = mem" #SZ "(%1++I:circ(m1))\n\t" \
+ : "=r"(RES), "+r"(PTR) \
+ : "r"((((INC) & 0x7f) << 17) | ((LEN) & 0x1ffff)), \
+ "r"(START) \
+ : "r4", "m1", "cs1")
+#define BxW_LOAD_pcr_Z(RES, PTR, START, LEN, INC) \
+ BxW_LOAD_pcr(ubh, RES, PTR, START, LEN, INC)
+#define BxW_LOAD_pcr_S(RES, PTR, START, LEN, INC) \
+ BxW_LOAD_pcr(bh, RES, PTR, START, LEN, INC)
+
+#define TEST_pcr(NAME, TYPE, SIGN, SIZE, LEN, INC, \
+ EXT, RES1, RES2, RES3, RES4) \
+void test_##NAME(void) \
+{ \
+ TYPE result; \
+ void *ptr = buf; \
+ init_buf(); \
+ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES1) | (EXT)); \
+ checkp(ptr, &buf[(1 * (INC) * (SIZE)) % (LEN)]); \
+ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES2) | (EXT)); \
+ checkp(ptr, &buf[(2 * (INC) * (SIZE)) % (LEN)]); \
+ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES3) | (EXT)); \
+ checkp(ptr, &buf[(3 * (INC) * (SIZE)) % (LEN)]); \
+ BxW_LOAD_pcr_##SIGN(result, ptr, buf, (LEN), (INC)); \
+ check(result, (RES4) | (EXT)); \
+ checkp(ptr, &buf[(4 * (INC) * (SIZE)) % (LEN)]); \
+}
+
+TEST_pcr(loadbzw2_pcr, int, Z, 2, 8, 2, 0x00000000,
+ 0x00020081, 0x00060085, 0x00020081, 0x00060085)
+TEST_pcr(loadbsw2_pcr, int, S, 2, 8, 2, 0x0000ff00,
+ 0x00020081, 0x00060085, 0x00020081, 0x00060085)
+TEST_pcr(loadbzw4_pcr, long long, Z, 4, 8, 1, 0x0000000000000000LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0004008300020081LL, 0x0008008700060085LL)
+TEST_pcr(loadbsw4_pcr, long long, S, 4, 8, 1, 0x0000ff000000ff00LL,
+ 0x0004008300020081LL, 0x0008008700060085LL,
+ 0x0004008300020081LL, 0x0008008700060085LL)
+
+int main()
+{
+ test_loadbzw2_io();
+ test_loadbsw2_io();
+ test_loadbzw4_io();
+ test_loadbsw4_io();
+
+ test_loadbzw2_ur();
+ test_loadbsw2_ur();
+ test_loadbzw4_ur();
+ test_loadbsw4_ur();
+
+ test_loadbzw2_ap();
+ test_loadbsw2_ap();
+ test_loadbzw4_ap();
+ test_loadbsw4_ap();
+
+ test_loadbzw2_pr();
+ test_loadbsw2_pr();
+ test_loadbzw4_pr();
+ test_loadbsw4_pr();
+
+ test_loadbzw2_pbr();
+ test_loadbsw2_pbr();
+ test_loadbzw4_pbr();
+ test_loadbsw4_pbr();
+
+ test_loadbzw2_pi();
+ test_loadbsw2_pi();
+ test_loadbzw4_pi();
+ test_loadbsw4_pi();
+
+ test_loadbzw2_pci();
+ test_loadbsw2_pci();
+ test_loadbzw4_pci();
+ test_loadbsw4_pci();
+
+ test_loadbzw2_pcr();
+ test_loadbsw2_pcr();
+ test_loadbzw4_pcr();
+ test_loadbsw4_pcr();
+
+ puts(err ? "FAIL" : "PASS");
+ return err ? 1 : 0;
+}
diff --git a/tests/tcg/hexagon/misc.c b/tests/tcg/hexagon/misc.c
index 458759f7b1..17c39198fc 100644
--- a/tests/tcg/hexagon/misc.c
+++ b/tests/tcg/hexagon/misc.c
@@ -231,6 +231,14 @@ static void check(int val, int expect)
}
}
+static void check64(long long val, long long expect)
+{
+ if (val != expect) {
+ printf("ERROR: 0x%016llx != 0x%016llx\n", val, expect);
+ err++;
+ }
+}
+
uint32_t init[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
uint32_t array[10];
@@ -264,8 +272,36 @@ static long long creg_pair(int x, int y)
return retval;
}
+static long long decbin(long long x, long long y, int *pred)
+{
+ long long retval;
+ asm ("%0 = decbin(%2, %3)\n\t"
+ "%1 = p0\n\t"
+ : "=r"(retval), "=r"(*pred)
+ : "r"(x), "r"(y));
+ return retval;
+}
+
+/* Check that predicates are auto-and'ed in a packet */
+static int auto_and(void)
+{
+ int retval;
+ asm ("r5 = #1\n\t"
+ "{\n\t"
+ " p0 = cmp.eq(r1, #1)\n\t"
+ " p0 = cmp.eq(r1, #2)\n\t"
+ "}\n\t"
+ "%0 = p0\n\t"
+ : "=r"(retval)
+ :
+ : "r5", "p0");
+ return retval;
+}
+
int main()
{
+ long long res64;
+ int pred;
memcpy(array, init, sizeof(array));
S4_storerhnew_rr(array, 4, 0xffff);
@@ -375,6 +411,17 @@ int main()
res = test_clrtnew(2, 7);
check(res, 7);
+ res64 = decbin(0xf0f1f2f3f4f5f6f7LL, 0x7f6f5f4f3f2f1f0fLL, &pred);
+ check64(res64, 0x357980003700010cLL);
+ check(pred, 0);
+
+ res64 = decbin(0xfLL, 0x1bLL, &pred);
+ check64(res64, 0x78000100LL);
+ check(pred, 1);
+
+ res = auto_and();
+ check(res, 0);
+
puts(err ? "FAIL" : "PASS");
return err;
}
diff --git a/tests/tcg/hexagon/multi_result.c b/tests/tcg/hexagon/multi_result.c
new file mode 100644
index 0000000000..52997b3128
--- /dev/null
+++ b/tests/tcg/hexagon/multi_result.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+
+static int sfrecipa(int Rs, int Rt, int *pred_result)
+{
+ int result;
+ int predval;
+
+ asm volatile("%0,p0 = sfrecipa(%2, %3)\n\t"
+ "%1 = p0\n\t"
+ : "+r"(result), "=r"(predval)
+ : "r"(Rs), "r"(Rt)
+ : "p0");
+ *pred_result = predval;
+ return result;
+}
+
+static int sfinvsqrta(int Rs, int *pred_result)
+{
+ int result;
+ int predval;
+
+ asm volatile("%0,p0 = sfinvsqrta(%2)\n\t"
+ "%1 = p0\n\t"
+ : "+r"(result), "=r"(predval)
+ : "r"(Rs)
+ : "p0");
+ *pred_result = predval;
+ return result;
+}
+
+static long long vacsh(long long Rxx, long long Rss, long long Rtt,
+ int *pred_result, int *ovf_result)
+{
+ long long result = Rxx;
+ int predval;
+ int usr;
+
+ /*
+ * This instruction can set bit 0 (OVF/overflow) in usr
+ * Clear the bit first, then return that bit to the caller
+ */
+ asm volatile("r2 = usr\n\t"
+ "r2 = clrbit(r2, #0)\n\t" /* clear overflow bit */
+ "usr = r2\n\t"
+ "%0,p0 = vacsh(%3, %4)\n\t"
+ "%1 = p0\n\t"
+ "%2 = usr\n\t"
+ : "+r"(result), "=r"(predval), "=r"(usr)
+ : "r"(Rss), "r"(Rtt)
+ : "r2", "p0", "usr");
+ *pred_result = predval;
+ *ovf_result = (usr & 1);
+ return result;
+}
+
+static long long vminub(long long Rtt, long long Rss,
+ int *pred_result)
+{
+ long long result;
+ int predval;
+
+ asm volatile("%0,p0 = vminub(%2, %3)\n\t"
+ "%1 = p0\n\t"
+ : "=r"(result), "=r"(predval)
+ : "r"(Rtt), "r"(Rss)
+ : "p0");
+ *pred_result = predval;
+ return result;
+}
+
+static long long add_carry(long long Rss, long long Rtt,
+ int pred_in, int *pred_result)
+{
+ long long result;
+ int predval = pred_in;
+
+ asm volatile("p0 = %1\n\t"
+ "%0 = add(%2, %3, p0):carry\n\t"
+ "%1 = p0\n\t"
+ : "=r"(result), "+r"(predval)
+ : "r"(Rss), "r"(Rtt)
+ : "p0");
+ *pred_result = predval;
+ return result;
+}
+
+static long long sub_carry(long long Rss, long long Rtt,
+ int pred_in, int *pred_result)
+{
+ long long result;
+ int predval = pred_in;
+
+ asm volatile("p0 = !cmp.eq(%1, #0)\n\t"
+ "%0 = sub(%2, %3, p0):carry\n\t"
+ "%1 = p0\n\t"
+ : "=r"(result), "+r"(predval)
+ : "r"(Rss), "r"(Rtt)
+ : "p0");
+ *pred_result = predval;
+ return result;
+}
+
+int err;
+
+static void check_ll(long long val, long long expect)
+{
+ if (val != expect) {
+ printf("ERROR: 0x%016llx != 0x%016llx\n", val, expect);
+ err++;
+ }
+}
+
+static void check(int val, int expect)
+{
+ if (val != expect) {
+ printf("ERROR: 0x%08x != 0x%08x\n", val, expect);
+ err++;
+ }
+}
+
+static void check_p(int val, int expect)
+{
+ if (val != expect) {
+ printf("ERROR: 0x%02x != 0x%02x\n", val, expect);
+ err++;
+ }
+}
+
+static void test_sfrecipa()
+{
+ int res;
+ int pred_result;
+
+ res = sfrecipa(0x04030201, 0x05060708, &pred_result);
+ check(res, 0x59f38001);
+ check_p(pred_result, 0x00);
+}
+
+static void test_sfinvsqrta()
+{
+ int res;
+ int pred_result;
+
+ res = sfinvsqrta(0x04030201, &pred_result);
+ check(res, 0x4d330000);
+ check_p(pred_result, 0xe0);
+
+ res = sfinvsqrta(0x0, &pred_result);
+ check(res, 0x3f800000);
+ check_p(pred_result, 0x0);
+}
+
+static void test_vacsh()
+{
+ long long res64;
+ int pred_result;
+ int ovf_result;
+
+ res64 = vacsh(0x0004000300020001LL,
+ 0x0001000200030004LL,
+ 0x0000000000000000LL, &pred_result, &ovf_result);
+ check_ll(res64, 0x0004000300030004LL);
+ check_p(pred_result, 0xf0);
+ check(ovf_result, 0);
+
+ res64 = vacsh(0x0004000300020001LL,
+ 0x0001000200030004LL,
+ 0x000affff000d0000LL, &pred_result, &ovf_result);
+ check_ll(res64, 0x000e0003000f0004LL);
+ check_p(pred_result, 0xcc);
+ check(ovf_result, 0);
+
+ res64 = vacsh(0x00047fff00020001LL,
+ 0x00017fff00030004LL,
+ 0x000a0fff000d0000LL, &pred_result, &ovf_result);
+ check_ll(res64, 0x000e7fff000f0004LL);
+ check_p(pred_result, 0xfc);
+ check(ovf_result, 1);
+
+ res64 = vacsh(0x0004000300020001LL,
+ 0x0001000200030009LL,
+ 0x000affff000d0001LL, &pred_result, &ovf_result);
+ check_ll(res64, 0x000e0003000f0008LL);
+ check_p(pred_result, 0xcc);
+ check(ovf_result, 0);
+}
+
+static void test_vminub()
+{
+ long long res64;
+ int pred_result;
+
+ res64 = vminub(0x0807060504030201LL,
+ 0x0102030405060708LL,
+ &pred_result);
+ check_ll(res64, 0x0102030404030201LL);
+ check_p(pred_result, 0xf0);
+
+ res64 = vminub(0x0802060405030701LL,
+ 0x0107030504060208LL,
+ &pred_result);
+ check_ll(res64, 0x0102030404030201LL);
+ check_p(pred_result, 0xaa);
+}
+
+static void test_add_carry()
+{
+ long long res64;
+ int pred_result;
+
+ res64 = add_carry(0x0000000000000000LL,
+ 0xffffffffffffffffLL,
+ 1, &pred_result);
+ check_ll(res64, 0x0000000000000000LL);
+ check_p(pred_result, 0xff);
+
+ res64 = add_carry(0x0000000100000000LL,
+ 0xffffffffffffffffLL,
+ 0, &pred_result);
+ check_ll(res64, 0x00000000ffffffffLL);
+ check_p(pred_result, 0xff);
+
+ res64 = add_carry(0x0000000100000000LL,
+ 0xffffffffffffffffLL,
+ 0, &pred_result);
+ check_ll(res64, 0x00000000ffffffffLL);
+ check_p(pred_result, 0xff);
+}
+
+static void test_sub_carry()
+{
+ long long res64;
+ int pred_result;
+
+ res64 = sub_carry(0x0000000000000000LL,
+ 0x0000000000000000LL,
+ 1, &pred_result);
+ check_ll(res64, 0x0000000000000000LL);
+ check_p(pred_result, 0xff);
+
+ res64 = sub_carry(0x0000000100000000LL,
+ 0x0000000000000000LL,
+ 0, &pred_result);
+ check_ll(res64, 0x00000000ffffffffLL);
+ check_p(pred_result, 0xff);
+
+ res64 = sub_carry(0x0000000100000000LL,
+ 0x0000000000000000LL,
+ 0, &pred_result);
+ check_ll(res64, 0x00000000ffffffffLL);
+ check_p(pred_result, 0xff);
+}
+
+int main()
+{
+ test_sfrecipa();
+ test_sfinvsqrta();
+ test_vacsh();
+ test_vminub();
+ test_add_carry();
+ test_sub_carry();
+
+ puts(err ? "FAIL" : "PASS");
+ return err;
+}
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
index 8a29e33e00..892f7f47d8 100644
--- a/tests/unit/test-bdrv-drain.c
+++ b/tests/unit/test-bdrv-drain.c
@@ -1478,7 +1478,6 @@ static void test_append_to_drained(void)
g_assert_cmpint(base_s->drain_count, ==, 1);
g_assert_cmpint(base->in_flight, ==, 0);
- /* Takes ownership of overlay, so we don't have to unref it later */
bdrv_append(overlay, base, &error_abort);
g_assert_cmpint(base->in_flight, ==, 0);
g_assert_cmpint(overlay->in_flight, ==, 0);
@@ -1495,6 +1494,7 @@ static void test_append_to_drained(void)
g_assert_cmpint(overlay->quiesce_counter, ==, 0);
g_assert_cmpint(overlay_s->drain_count, ==, 0);
+ bdrv_unref(overlay);
bdrv_unref(base);
blk_unref(blk);
}
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
index c4f7d16039..88f25c0cdb 100644
--- a/tests/unit/test-bdrv-graph-mod.c
+++ b/tests/unit/test-bdrv-graph-mod.c
@@ -1,7 +1,7 @@
/*
* Block node graph modifications tests
*
- * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
+ * Copyright (c) 2019-2021 Virtuozzo International GmbH. All rights reserved.
*
* 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
@@ -44,6 +44,21 @@ static BlockDriver bdrv_no_perm = {
.bdrv_child_perm = no_perm_default_perms,
};
+static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ *nperm = BLK_PERM_WRITE;
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
+}
+
+static BlockDriver bdrv_exclusive_writer = {
+ .format_name = "exclusive-writer",
+ .bdrv_child_perm = exclusive_write_perms,
+};
+
static BlockDriverState *no_perm_node(const char *name)
{
return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
@@ -55,6 +70,12 @@ static BlockDriverState *pass_through_node(const char *name)
BDRV_O_RDWR, &error_abort);
}
+static BlockDriverState *exclusive_writer_node(const char *name)
+{
+ return bdrv_new_open_driver(&bdrv_exclusive_writer, name,
+ BDRV_O_RDWR, &error_abort);
+}
+
/*
* test_update_perm_tree
*
@@ -117,6 +138,7 @@ static void test_update_perm_tree(void)
ret = bdrv_append(filter, bs, NULL);
g_assert_cmpint(ret, <, 0);
+ bdrv_unref(filter);
blk_unref(root);
}
@@ -181,10 +203,189 @@ static void test_should_update_child(void)
bdrv_append(filter, bs, &error_abort);
g_assert(target->backing->bs == bs);
+ bdrv_unref(filter);
bdrv_unref(bs);
blk_unref(root);
}
+/*
+ * test_parallel_exclusive_write
+ *
+ * Check that when we replace node, old permissions of the node being removed
+ * doesn't break the replacement.
+ */
+static void test_parallel_exclusive_write(void)
+{
+ BlockDriverState *top = exclusive_writer_node("top");
+ BlockDriverState *base = no_perm_node("base");
+ BlockDriverState *fl1 = pass_through_node("fl1");
+ BlockDriverState *fl2 = pass_through_node("fl2");
+
+ /*
+ * bdrv_attach_child() eats child bs reference, so we need two @base
+ * references for two filters:
+ */
+ bdrv_ref(base);
+
+ bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA,
+ &error_abort);
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+
+ bdrv_replace_node(fl1, fl2, &error_abort);
+
+ bdrv_unref(fl2);
+ bdrv_unref(top);
+}
+
+static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ if (bs->file && c == bs->file) {
+ *nperm = BLK_PERM_WRITE;
+ *nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
+ } else {
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+ }
+}
+
+static BlockDriver bdrv_write_to_file = {
+ .format_name = "tricky-perm",
+ .bdrv_child_perm = write_to_file_perms,
+};
+
+
+/*
+ * The following test shows that topological-sort order is required for
+ * permission update, simple DFS is not enough.
+ *
+ * Consider the block driver which has two filter children: one active
+ * with exclusive write access and one inactive with no specific
+ * permissions.
+ *
+ * And, these two children has a common base child, like this:
+ *
+ * ┌─────┐ ┌──────┐
+ * │ fl2 │ ◀── │ top │
+ * └─────┘ └──────┘
+ * │ │
+ * │ │ w
+ * │ ▼
+ * │ ┌──────┐
+ * │ │ fl1 │
+ * │ └──────┘
+ * │ │
+ * │ │ w
+ * │ ▼
+ * │ ┌──────┐
+ * └───────▶ │ base │
+ * └──────┘
+ *
+ * So, exclusive write is propagated.
+ *
+ * Assume, we want to make fl2 active instead of fl1.
+ * So, we set some option for top driver and do permission update.
+ *
+ * With simple DFS, if permission update goes first through
+ * top->fl1->base branch it will succeed: it firstly drop exclusive write
+ * permissions and than apply them for another BdrvChildren.
+ * But if permission update goes first through top->fl2->base branch it
+ * will fail, as when we try to update fl2->base child, old not yet
+ * updated fl1->base child will be in conflict.
+ *
+ * With topological-sort order we always update parents before children, so fl1
+ * and fl2 are both updated when we update base and there is no conflict.
+ */
+static void test_parallel_perm_update(void)
+{
+ BlockDriverState *top = no_perm_node("top");
+ BlockDriverState *tricky =
+ bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR,
+ &error_abort);
+ BlockDriverState *base = no_perm_node("base");
+ BlockDriverState *fl1 = pass_through_node("fl1");
+ BlockDriverState *fl2 = pass_through_node("fl2");
+ BdrvChild *c_fl1, *c_fl2;
+
+ /*
+ * bdrv_attach_child() eats child bs reference, so we need two @base
+ * references for two filters:
+ */
+ bdrv_ref(base);
+
+ bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
+ &error_abort);
+ c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds,
+ BDRV_CHILD_FILTERED, &error_abort);
+ c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds,
+ BDRV_CHILD_FILTERED, &error_abort);
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ &error_abort);
+
+ /* Select fl1 as first child to be active */
+ tricky->file = c_fl1;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl1->perm & BLK_PERM_WRITE);
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
+
+ /* Now, try to switch active child and update permissions */
+ tricky->file = c_fl2;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl2->perm & BLK_PERM_WRITE);
+ assert(!(c_fl1->perm & BLK_PERM_WRITE));
+
+ /* Switch once more, to not care about real child order in the list */
+ tricky->file = c_fl1;
+ bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
+
+ assert(c_fl1->perm & BLK_PERM_WRITE);
+ assert(!(c_fl2->perm & BLK_PERM_WRITE));
+
+ bdrv_unref(top);
+}
+
+/*
+ * It's possible that filter required permissions allows to insert it to backing
+ * chain, like:
+ *
+ * 1. [top] -> [filter] -> [base]
+ *
+ * but doesn't allow to add it as a branch:
+ *
+ * 2. [filter] --\
+ * v
+ * [top] -> [base]
+ *
+ * So, inserting such filter should do all graph modifications and only then
+ * update permissions. If we try to go through intermediate state [2] and update
+ * permissions on it we'll fail.
+ *
+ * Let's check that bdrv_append() can append such a filter.
+ */
+static void test_append_greedy_filter(void)
+{
+ BlockDriverState *top = exclusive_writer_node("top");
+ BlockDriverState *base = no_perm_node("base");
+ BlockDriverState *fl = exclusive_writer_node("fl1");
+
+ bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW,
+ &error_abort);
+
+ bdrv_append(fl, base, &error_abort);
+ bdrv_unref(fl);
+ bdrv_unref(top);
+}
+
int main(int argc, char *argv[])
{
bdrv_init();
@@ -195,6 +396,12 @@ int main(int argc, char *argv[])
g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
g_test_add_func("/bdrv-graph-mod/should-update-child",
test_should_update_child);
+ g_test_add_func("/bdrv-graph-mod/parallel-perm-update",
+ test_parallel_perm_update);
+ g_test_add_func("/bdrv-graph-mod/parallel-exclusive-write",
+ test_parallel_exclusive_write);
+ g_test_add_func("/bdrv-graph-mod/append-greedy-filter",
+ test_append_greedy_filter);
return g_test_run();
}
diff --git a/util/meson.build b/util/meson.build
index 510765cde4..97fad44105 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -41,6 +41,7 @@ util_ss.add(files('qsp.c'))
util_ss.add(files('range.c'))
util_ss.add(files('stats64.c'))
util_ss.add(files('systemd.c'))
+util_ss.add(files('transactions.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('drm.c'))
util_ss.add(files('guest-random.c'))
util_ss.add(files('yank.c'))
diff --git a/util/transactions.c b/util/transactions.c
new file mode 100644
index 0000000000..d0bc9a3e73
--- /dev/null
+++ b/util/transactions.c
@@ -0,0 +1,96 @@
+/*
+ * Simple transactions API
+ *
+ * Copyright (c) 2021 Virtuozzo International GmbH.
+ *
+ * Author:
+ * Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
+ *
+ * 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/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu/transactions.h"
+#include "qemu/queue.h"
+
+typedef struct TransactionAction {
+ TransactionActionDrv *drv;
+ void *opaque;
+ QSLIST_ENTRY(TransactionAction) entry;
+} TransactionAction;
+
+struct Transaction {
+ QSLIST_HEAD(, TransactionAction) actions;
+};
+
+Transaction *tran_new(void)
+{
+ Transaction *tran = g_new(Transaction, 1);
+
+ QSLIST_INIT(&tran->actions);
+
+ return tran;
+}
+
+void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque)
+{
+ TransactionAction *act;
+
+ act = g_new(TransactionAction, 1);
+ *act = (TransactionAction) {
+ .drv = drv,
+ .opaque = opaque
+ };
+
+ QSLIST_INSERT_HEAD(&tran->actions, act, entry);
+}
+
+void tran_abort(Transaction *tran)
+{
+ TransactionAction *act, *next;
+
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
+ if (act->drv->abort) {
+ act->drv->abort(act->opaque);
+ }
+
+ if (act->drv->clean) {
+ act->drv->clean(act->opaque);
+ }
+
+ g_free(act);
+ }
+
+ g_free(tran);
+}
+
+void tran_commit(Transaction *tran)
+{
+ TransactionAction *act, *next;
+
+ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) {
+ if (act->drv->commit) {
+ act->drv->commit(act->opaque);
+ }
+
+ if (act->drv->clean) {
+ act->drv->clean(act->opaque);
+ }
+
+ g_free(act);
+ }
+
+ g_free(tran);
+}