From e8b9cb8b4a3331a3d8a2156d000fb18a6f649d17 Mon Sep 17 00:00:00 2001
From: Richard Henderson <richard.henderson@linaro.org>
Date: Thu, 25 Feb 2021 14:59:34 -0500
Subject: [PATCH] target/arm: Implement LDG, STG, ST2G instructions

Backports commit c15294c1e36a7dd9b25bd54d98178e80f4b64bc1 from qemu
---
 qemu/aarch64.h                  |   8 ++
 qemu/aarch64eb.h                |   8 ++
 qemu/arm.h                      |   1 +
 qemu/armeb.h                    |   1 +
 qemu/header_gen.py              |   9 ++
 qemu/target/arm/helper-a64.h    |   9 +-
 qemu/target/arm/helper.h        |   4 +
 qemu/target/arm/op_helper.c     |  16 +++
 qemu/target/arm/translate-a64.c | 175 +++++++++++++++++++++++++++++++-
 9 files changed, 225 insertions(+), 6 deletions(-)

diff --git a/qemu/aarch64.h b/qemu/aarch64.h
index 9a415407..6dc87081 100644
--- a/qemu/aarch64.h
+++ b/qemu/aarch64.h
@@ -3577,6 +3577,7 @@
 #define helper_gvec_usra_h helper_gvec_usra_h_aarch64
 #define helper_gvec_usra_s helper_gvec_usra_s_aarch64
 #define helper_irg helper_irg_aarch64
+#define helper_ldg helper_ldg_aarch64
 #define helper_msr_i_daifclear helper_msr_i_daifclear_aarch64
 #define helper_msr_i_daifset helper_msr_i_daifset_aarch64
 #define helper_msr_i_spsel helper_msr_i_spsel_aarch64
@@ -3596,6 +3597,7 @@
 #define helper_paired_cmpxchg64_be_parallel helper_paired_cmpxchg64_be_parallel_aarch64
 #define helper_paired_cmpxchg64_le helper_paired_cmpxchg64_le_aarch64
 #define helper_paired_cmpxchg64_le_parallel helper_paired_cmpxchg64_le_parallel_aarch64
+#define helper_probe_access_armfn helper_probe_access_armfn_aarch64
 #define helper_rbit64 helper_rbit64_aarch64
 #define helper_recpsf_f16 helper_recpsf_f16_aarch64
 #define helper_recpsf_f32 helper_recpsf_f32_aarch64
@@ -3606,6 +3608,12 @@
 #define helper_sdiv64 helper_sdiv64_aarch64
 #define helper_simd_tbl helper_simd_tbl_aarch64
 #define helper_sqrt_f16 helper_sqrt_f16_aarch64
+#define helper_st2g helper_st2g_aarch64
+#define helper_st2g_parallel helper_st2g_parallel_aarch64
+#define helper_st2g_stub helper_st2g_stub_aarch64
+#define helper_stg helper_stg_aarch64
+#define helper_stg_parallel helper_stg_parallel_aarch64
+#define helper_stg_stub helper_stg_stub_aarch64
 #define helper_sve_abs_b helper_sve_abs_b_aarch64
 #define helper_sve_abs_d helper_sve_abs_d_aarch64
 #define helper_sve_abs_h helper_sve_abs_h_aarch64
diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h
index 6e98f71c..8cc106b9 100644
--- a/qemu/aarch64eb.h
+++ b/qemu/aarch64eb.h
@@ -3577,6 +3577,7 @@
 #define helper_gvec_usra_h helper_gvec_usra_h_aarch64eb
 #define helper_gvec_usra_s helper_gvec_usra_s_aarch64eb
 #define helper_irg helper_irg_aarch64eb
+#define helper_ldg helper_ldg_aarch64eb
 #define helper_msr_i_daifclear helper_msr_i_daifclear_aarch64eb
 #define helper_msr_i_daifset helper_msr_i_daifset_aarch64eb
 #define helper_msr_i_spsel helper_msr_i_spsel_aarch64eb
@@ -3596,6 +3597,7 @@
 #define helper_paired_cmpxchg64_be_parallel helper_paired_cmpxchg64_be_parallel_aarch64eb
 #define helper_paired_cmpxchg64_le helper_paired_cmpxchg64_le_aarch64eb
 #define helper_paired_cmpxchg64_le_parallel helper_paired_cmpxchg64_le_parallel_aarch64eb
+#define helper_probe_access_armfn helper_probe_access_armfn_aarch64eb
 #define helper_rbit64 helper_rbit64_aarch64eb
 #define helper_recpsf_f16 helper_recpsf_f16_aarch64eb
 #define helper_recpsf_f32 helper_recpsf_f32_aarch64eb
@@ -3606,6 +3608,12 @@
 #define helper_sdiv64 helper_sdiv64_aarch64eb
 #define helper_simd_tbl helper_simd_tbl_aarch64eb
 #define helper_sqrt_f16 helper_sqrt_f16_aarch64eb
+#define helper_st2g helper_st2g_aarch64eb
+#define helper_st2g_parallel helper_st2g_parallel_aarch64eb
+#define helper_st2g_stub helper_st2g_stub_aarch64eb
+#define helper_stg helper_stg_aarch64eb
+#define helper_stg_parallel helper_stg_parallel_aarch64eb
+#define helper_stg_stub helper_stg_stub_aarch64eb
 #define helper_sve_abs_b helper_sve_abs_b_aarch64eb
 #define helper_sve_abs_d helper_sve_abs_d_aarch64eb
 #define helper_sve_abs_h helper_sve_abs_h_aarch64eb
diff --git a/qemu/arm.h b/qemu/arm.h
index 7924c55e..c64d5472 100644
--- a/qemu/arm.h
+++ b/qemu/arm.h
@@ -3512,6 +3512,7 @@
 #define helper_gvec_usra_d helper_gvec_usra_d_arm
 #define helper_gvec_usra_h helper_gvec_usra_h_arm
 #define helper_gvec_usra_s helper_gvec_usra_s_arm
+#define helper_probe_access_armfn helper_probe_access_armfn_arm
 #define helper_vjcvt helper_vjcvt_arm
 #define pmu_init pmu_init_arm
 #define pmsav8_mpu_lookup pmsav8_mpu_lookup_arm
diff --git a/qemu/armeb.h b/qemu/armeb.h
index 2f6ba0a4..597671e5 100644
--- a/qemu/armeb.h
+++ b/qemu/armeb.h
@@ -3512,6 +3512,7 @@
 #define helper_gvec_usra_d helper_gvec_usra_d_armeb
 #define helper_gvec_usra_h helper_gvec_usra_h_armeb
 #define helper_gvec_usra_s helper_gvec_usra_s_armeb
+#define helper_probe_access_armfn helper_probe_access_armfn_armeb
 #define helper_vjcvt helper_vjcvt_armeb
 #define pmu_init pmu_init_armeb
 #define pmsav8_mpu_lookup pmsav8_mpu_lookup_armeb
diff --git a/qemu/header_gen.py b/qemu/header_gen.py
index 3b3579bf..b53953d1 100644
--- a/qemu/header_gen.py
+++ b/qemu/header_gen.py
@@ -3521,6 +3521,7 @@ arm_symbols = (
     'helper_gvec_usra_d',
     'helper_gvec_usra_h',
     'helper_gvec_usra_s',
+    'helper_probe_access_armfn',
     'helper_vjcvt',
     'pmu_init',
     'pmsav8_mpu_lookup',
@@ -3711,6 +3712,7 @@ aarch64_symbols = (
     'helper_gvec_usra_h',
     'helper_gvec_usra_s',
     'helper_irg',
+    'helper_ldg',
     'helper_msr_i_daifclear',
     'helper_msr_i_daifset',
     'helper_msr_i_spsel',
@@ -3730,6 +3732,7 @@ aarch64_symbols = (
     'helper_paired_cmpxchg64_be_parallel',
     'helper_paired_cmpxchg64_le',
     'helper_paired_cmpxchg64_le_parallel',
+    'helper_probe_access_armfn',
     'helper_rbit64',
     'helper_recpsf_f16',
     'helper_recpsf_f32',
@@ -3740,6 +3743,12 @@ aarch64_symbols = (
     'helper_sdiv64',
     'helper_simd_tbl',
     'helper_sqrt_f16',
+    'helper_st2g',
+    'helper_st2g_parallel',
+    'helper_st2g_stub',
+    'helper_stg',
+    'helper_stg_parallel',
+    'helper_stg_stub',
     'helper_sve_abs_b',
     'helper_sve_abs_d',
     'helper_sve_abs_h',
diff --git a/qemu/target/arm/helper-a64.h b/qemu/target/arm/helper-a64.h
index e3fb1928..13f09433 100644
--- a/qemu/target/arm/helper-a64.h
+++ b/qemu/target/arm/helper-a64.h
@@ -105,4 +105,11 @@ 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(irg, TCG_CALL_NO_RWG, i64, env, i64, i64)
-DEF_HELPER_FLAGS_4(addsubg, TCG_CALL_NO_RWG_SE, i64, env, i64, s32, i32)
\ No newline at end of file
+DEF_HELPER_FLAGS_4(addsubg, TCG_CALL_NO_RWG_SE, i64, env, i64, s32, i32)
+DEF_HELPER_FLAGS_3(ldg, TCG_CALL_NO_WG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(stg, TCG_CALL_NO_WG, void, env, i64, i64)
+DEF_HELPER_FLAGS_3(stg_parallel, TCG_CALL_NO_WG, void, env, i64, i64)
+DEF_HELPER_FLAGS_2(stg_stub, TCG_CALL_NO_WG, void, env, i64)
+DEF_HELPER_FLAGS_3(st2g, TCG_CALL_NO_WG, void, env, i64, i64)
+DEF_HELPER_FLAGS_3(st2g_parallel, TCG_CALL_NO_WG, void, env, i64, i64)
+DEF_HELPER_FLAGS_2(st2g_stub, TCG_CALL_NO_WG, void, env, i64)
\ No newline at end of file
diff --git a/qemu/target/arm/helper.h b/qemu/target/arm/helper.h
index 4ffb145b..2e1f9e20 100644
--- a/qemu/target/arm/helper.h
+++ b/qemu/target/arm/helper.h
@@ -92,6 +92,10 @@ DEF_HELPER_4(msr_banked, void, env, i32, i32, i32)
 DEF_HELPER_2(get_user_reg, i32, env, i32)
 DEF_HELPER_3(set_user_reg, void, env, i32, i32)
 
+// Is "probe_access" in mainline qemu. We rename it to work
+// around header_gen.py shenanigans.
+DEF_HELPER_FLAGS_5(probe_access_armfn, TCG_CALL_NO_WG, void, env, tl, i32, i32, i32)
+
 DEF_HELPER_1(vfp_get_fpscr, i32, env)
 DEF_HELPER_2(vfp_set_fpscr, void, env, i32)
 
diff --git a/qemu/target/arm/op_helper.c b/qemu/target/arm/op_helper.c
index c3ea418e..2f2f1e9a 100644
--- a/qemu/target/arm/op_helper.c
+++ b/qemu/target/arm/op_helper.c
@@ -927,3 +927,19 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i)
         return ((uint32_t)x >> shift) | (x << (32 - shift));
     }
 }
+
+void HELPER(probe_access_armfn)(CPUARMState *env, target_ulong ptr,
+                                uint32_t access_type, uint32_t mmu_idx,
+                                uint32_t size)
+{
+    uint32_t in_page = -((uint32_t)ptr | TARGET_PAGE_SIZE);
+    uintptr_t ra = GETPC();
+
+    if (likely(size <= in_page)) {
+        probe_access(env, ptr, size, access_type, mmu_idx, ra);
+    } else {
+        probe_access(env, ptr, in_page, access_type, mmu_idx, ra);
+        probe_access(env, ptr + in_page, size - in_page,
+                     access_type, mmu_idx, ra);
+    }
+}
diff --git a/qemu/target/arm/translate-a64.c b/qemu/target/arm/translate-a64.c
index 8afdd768..33cb474c 100644
--- a/qemu/target/arm/translate-a64.c
+++ b/qemu/target/arm/translate-a64.c
@@ -369,6 +369,21 @@ static void gen_address_with_allocation_tag0(TCGContext *s, TCGv_i64 dst, TCGv_i
     tcg_gen_andi_i64(s, dst, src, ~MAKE_64BIT_MASK(56, 4));
 }
 
+static void gen_probe_access(DisasContext *s, TCGv_i64 ptr,
+                             MMUAccessType acc, int log2_size)
+{
+    TCGContext *tcg_ctx = s->uc->tcg_ctx;
+    TCGv_i32 t_acc = tcg_const_i32(tcg_ctx, acc);
+    TCGv_i32 t_idx = tcg_const_i32(tcg_ctx, get_mem_index(s));
+    TCGv_i32 t_size = tcg_const_i32(tcg_ctx, 1 << log2_size);
+
+    gen_helper_probe_access_armfn(tcg_ctx, tcg_ctx->cpu_env, ptr, t_acc, t_idx, t_size);
+    tcg_temp_free_i32(tcg_ctx, t_acc);
+    tcg_temp_free_i32(tcg_ctx, t_idx);
+    tcg_temp_free_i32(tcg_ctx, t_size);
+}
+
+
 typedef struct DisasCompare64 {
     TCGCond cond;
     TCGv_i64 value;
@@ -3879,6 +3894,155 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
     }
 }
 
+/*
+ * Load/Store memory tags
+ *
+ *  31 30 29         24     22  21     12    10      5      0
+ * +-----+-------------+-----+---+------+-----+------+------+
+ * | 1 1 | 0 1 1 0 0 1 | op1 | 1 | imm9 | op2 |  Rn  |  Rt  |
+ * +-----+-------------+-----+---+------+-----+------+------+
+ */
+static void disas_ldst_tag(DisasContext *s, uint32_t insn)
+{
+    TCGContext *tcg_ctx = s->uc->tcg_ctx;
+    int rt = extract32(insn, 0, 5);
+    int rn = extract32(insn, 5, 5);
+    uint64_t offset = sextract64(insn, 12, 9) << LOG2_TAG_GRANULE;
+    int op2 = extract32(insn, 10, 2);
+    int op1 = extract32(insn, 22, 2);
+    bool is_load = false, is_pair = false, is_zero = false;
+    int index = 0;
+    TCGv_i64 addr, clean_addr, tcg_rt;
+
+    /* We checked insn bits [29:24,21] in the caller.  */
+    if (extract32(insn, 30, 2) != 3) {
+        goto do_unallocated;
+    }
+
+    /*
+     * @index is a tri-state variable which has 3 states:
+     * < 0 : post-index, writeback
+     * = 0 : signed offset
+     * > 0 : pre-index, writeback
+     */
+    switch (op1) {
+    case 0:
+        if (op2 != 0) {
+            /* STG */
+            index = op2 - 2;
+            break;
+        }
+        goto do_unallocated;
+    case 1:
+        if (op2 != 0) {
+            /* STZG */
+            is_zero = true;
+            index = op2 - 2;
+        } else {
+            /* LDG */
+            is_load = true;
+        }
+        break;
+    case 2:
+        if (op2 != 0) {
+            /* ST2G */
+            is_pair = true;
+            index = op2 - 2;
+            break;
+        }
+        goto do_unallocated;
+    case 3:
+        if (op2 != 0) {
+            /* STZ2G */
+            is_pair = is_zero = true;
+            index = op2 - 2;
+            break;
+        }
+        goto do_unallocated;
+
+    default:
+    do_unallocated:
+        unallocated_encoding(s);
+        return;
+    }
+
+    if (!dc_isar_feature(aa64_mte_insn_reg, s)) {
+        goto do_unallocated;
+    }
+
+    if (rn == 31) {
+        gen_check_sp_alignment(s);
+    }
+
+    addr = read_cpu_reg_sp(s, rn, true);
+    if (index >= 0) {
+        /* pre-index or signed offset */
+        tcg_gen_addi_i64(tcg_ctx, addr, addr, offset);
+    }
+
+    if (is_load) {
+        tcg_gen_andi_i64(tcg_ctx, addr, addr, -TAG_GRANULE);
+        tcg_rt = cpu_reg(s, rt);
+        if (s->ata) {
+            gen_helper_ldg(tcg_ctx, tcg_rt, tcg_ctx->cpu_env, addr, tcg_rt);
+        } else {
+            clean_addr = clean_data_tbi(s, addr);
+            gen_probe_access(s, clean_addr, MMU_DATA_LOAD, MO_8);
+            gen_address_with_allocation_tag0(tcg_ctx, tcg_rt, addr);
+        }
+    } else {
+        tcg_rt = cpu_reg_sp(s, rt);
+        if (!s->ata) {
+            /*
+             * For STG and ST2G, we need to check alignment and probe memory.
+             * TODO: For STZG and STZ2G, we could rely on the stores below,
+             * at least for system mode; user-only won't enforce alignment.
+             */
+            if (is_pair) {
+                gen_helper_st2g_stub(tcg_ctx, tcg_ctx->cpu_env, addr);
+            } else {
+                gen_helper_stg_stub(tcg_ctx, tcg_ctx->cpu_env, addr);
+            }
+        } else if (tb_cflags(s->base.tb) & CF_PARALLEL) {
+            if (is_pair) {
+                gen_helper_st2g_parallel(tcg_ctx, tcg_ctx->cpu_env, addr, tcg_rt);
+            } else {
+                gen_helper_stg_parallel(tcg_ctx, tcg_ctx->cpu_env, addr, tcg_rt);
+            }
+        } else {
+            if (is_pair) {
+                gen_helper_st2g(tcg_ctx, tcg_ctx->cpu_env, addr, tcg_rt);
+            } else {
+                gen_helper_stg(tcg_ctx, tcg_ctx->cpu_env, addr, tcg_rt);
+            }
+        }
+    }
+
+    if (is_zero) {
+        TCGv_i64 clean_addr = clean_data_tbi(s, addr);
+        TCGv_i64 tcg_zero = tcg_const_i64(tcg_ctx, 0);
+        int mem_index = get_mem_index(s);
+        int i, n = (1 + is_pair) << LOG2_TAG_GRANULE;
+
+        tcg_gen_qemu_st_i64(s->uc, tcg_zero, clean_addr, mem_index,
+                            MO_Q | MO_ALIGN_16);
+        for (i = 8; i < n; i += 8) {
+            tcg_gen_addi_i64(tcg_ctx, clean_addr, clean_addr, 8);
+            tcg_gen_qemu_st_i64(s->uc, tcg_zero, clean_addr, mem_index, MO_Q);
+        }
+        tcg_temp_free_i64(tcg_ctx, tcg_zero);
+    }
+
+    if (index != 0) {
+        /* pre-index or post-index */
+        if (index < 0) {
+            /* post-index */
+            tcg_gen_addi_i64(tcg_ctx, addr, addr, offset);
+        }
+        tcg_gen_mov_i64(tcg_ctx, cpu_reg_sp(s, rn), addr);
+    }
+}
+
 /* Loads and stores */
 static void disas_ldst(DisasContext *s, uint32_t insn)
 {
@@ -3903,13 +4067,14 @@ static void disas_ldst(DisasContext *s, uint32_t insn)
     case 0x0d: /* AdvSIMD load/store single structure */
         disas_ldst_single_struct(s, insn);
         break;
-    case 0x19: /* LDAPR/STLR (unscaled immediate) */
-        if (extract32(insn, 10, 2) != 0 ||
-            extract32(insn, 21, 1) != 0) {
+    case 0x19:
+        if (extract32(insn, 21, 1) != 0) {
+            disas_ldst_tag(s, insn);
+        } else if (extract32(insn, 10, 2) == 0) {
+            disas_ldst_ldapr_stlr(s, insn);
+        } else {
             unallocated_encoding(s);
-            break;
         }
-        disas_ldst_ldapr_stlr(s, insn);
         break;
     default:
         unallocated_encoding(s);