aboutsummaryrefslogtreecommitdiff
path: root/target/arm/translate-m-nocp.c
blob: 09b3be4ed31498891d4851d8b05ac79c1d973c25 (plain)
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
 *  ARM translation: M-profile NOCP special-case instructions
 *
 *  Copyright (c) 2020 Linaro, Ltd.
 *
 * 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 "tcg/tcg-op.h"
#include "translate.h"
#include "translate-a32.h"

#include "decode-m-nocp.c.inc"

/*
 * Decode VLLDM and VLSTM are nonstandard because:
 *  * if there is no FPU then these insns must NOP in
 *    Secure state and UNDEF in Nonsecure state
 *  * if there is an FPU then these insns do not have
 *    the usual behaviour that vfp_access_check() provides of
 *    being controlled by CPACR/NSACR enable bits or the
 *    lazy-stacking logic.
 */
static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a)
{
    TCGv_i32 fptr;

    if (!arm_dc_feature(s, ARM_FEATURE_M) ||
        !arm_dc_feature(s, ARM_FEATURE_V8)) {
        return false;
    }

    if (a->op) {
        /*
         * T2 encoding ({D0-D31} reglist): v8.1M and up. We choose not
         * to take the IMPDEF option to make memory accesses to the stack
         * slots that correspond to the D16-D31 registers (discarding
         * read data and writing UNKNOWN values), so for us the T2
         * encoding behaves identically to the T1 encoding.
         */
        if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
            return false;
        }
    } else {
        /*
         * T1 encoding ({D0-D15} reglist); undef if we have 32 Dregs.
         * This is currently architecturally impossible, but we add the
         * check to stay in line with the pseudocode. Note that we must
         * emit code for the UNDEF so it takes precedence over the NOCP.
         */
        if (dc_isar_feature(aa32_simd_r32, s)) {
            unallocated_encoding(s);
            return true;
        }
    }

    /*
     * If not secure, UNDEF. We must emit code for this
     * rather than returning false so that this takes
     * precedence over the m-nocp.decode NOCP fallback.
     */
    if (!s->v8m_secure) {
        unallocated_encoding(s);
        return true;
    }

    s->eci_handled = true;

    /* If no fpu, NOP. */
    if (!dc_isar_feature(aa32_vfp, s)) {
        clear_eci_state(s);
        return true;
    }

    fptr = load_reg(s, a->rn);
    if (a->l) {
        gen_helper_v7m_vlldm(cpu_env, fptr);
    } else {
        gen_helper_v7m_vlstm(cpu_env, fptr);
    }
    tcg_temp_free_i32(fptr);

    clear_eci_state(s);

    /* End the TB, because we have updated FP control bits */
    s->base.is_jmp = DISAS_UPDATE_EXIT;
    return true;
}

static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a)
{
    int btmreg, topreg;
    TCGv_i64 zero;
    TCGv_i32 aspen, sfpa;

    if (!dc_isar_feature(aa32_m_sec_state, s)) {
        /* Before v8.1M, fall through in decode to NOCP check */
        return false;
    }

    /* Explicitly UNDEF because this takes precedence over NOCP */
    if (!arm_dc_feature(s, ARM_FEATURE_M_MAIN) || !s->v8m_secure) {
        unallocated_encoding(s);
        return true;
    }

    s->eci_handled = true;

    if (!dc_isar_feature(aa32_vfp_simd, s)) {
        /* NOP if we have neither FP nor MVE */
        clear_eci_state(s);
        return true;
    }

    /*
     * If FPCCR.ASPEN != 0 && CONTROL_S.SFPA == 0 then there is no
     * active floating point context so we must NOP (without doing
     * any lazy state preservation or the NOCP check).
     */
    aspen = load_cpu_field(v7m.fpccr[M_REG_S]);
    sfpa = load_cpu_field(v7m.control[M_REG_S]);
    tcg_gen_andi_i32(aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
    tcg_gen_xori_i32(aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
    tcg_gen_andi_i32(sfpa, sfpa, R_V7M_CONTROL_SFPA_MASK);
    tcg_gen_or_i32(sfpa, sfpa, aspen);
    arm_gen_condlabel(s);
    tcg_gen_brcondi_i32(TCG_COND_EQ, sfpa, 0, s->condlabel);

    if (s->fp_excp_el != 0) {
        gen_exception_insn(s, s->pc_curr, EXCP_NOCP,
                           syn_uncategorized(), s->fp_excp_el);
        return true;
    }

    topreg = a->vd + a->imm - 1;
    btmreg = a->vd;

    /* Convert to Sreg numbers if the insn specified in Dregs */
    if (a->size == 3) {
        topreg = topreg * 2 + 1;
        btmreg *= 2;
    }

    if (topreg > 63 || (topreg > 31 && !(topreg & 1))) {
        /* UNPREDICTABLE: we choose to undef */
        unallocated_encoding(s);
        return true;
    }

    /* Silently ignore requests to clear D16-D31 if they don't exist */
    if (topreg > 31 && !dc_isar_feature(aa32_simd_r32, s)) {
        topreg = 31;
    }

    if (!vfp_access_check(s)) {
        return true;
    }

    /* Zero the Sregs from btmreg to topreg inclusive. */
    zero = tcg_const_i64(0);
    if (btmreg & 1) {
        write_neon_element64(zero, btmreg >> 1, 1, MO_32);
        btmreg++;
    }
    for (; btmreg + 1 <= topreg; btmreg += 2) {
        write_neon_element64(zero, btmreg >> 1, 0, MO_64);
    }
    if (btmreg == topreg) {
        write_neon_element64(zero, btmreg >> 1, 0, MO_32);
        btmreg++;
    }
    assert(btmreg == topreg + 1);
    if (dc_isar_feature(aa32_mve, s)) {
        TCGv_i32 z32 = tcg_const_i32(0);
        store_cpu_field(z32, v7m.vpr);
    }

    clear_eci_state(s);
    return true;
}

static bool trans_NOCP(DisasContext *s, arg_nocp *a)
{
    /*
     * Handle M-profile early check for disabled coprocessor:
     * all we need to do here is emit the NOCP exception if
     * the coprocessor is disabled. Otherwise we return false
     * and the real VFP/etc decode will handle the insn.
     */
    assert(arm_dc_feature(s, ARM_FEATURE_M));

    if (a->cp == 11) {
        a->cp = 10;
    }
    if (arm_dc_feature(s, ARM_FEATURE_V8_1M) &&
        (a->cp == 8 || a->cp == 9 || a->cp == 14 || a->cp == 15)) {
        /* in v8.1M cp 8, 9, 14, 15 also are governed by the cp10 enable */
        a->cp = 10;
    }

    if (a->cp != 10) {
        gen_exception_insn(s, s->pc_curr, EXCP_NOCP,
                           syn_uncategorized(), default_exception_el(s));
        return true;
    }

    if (s->fp_excp_el != 0) {
        gen_exception_insn(s, s->pc_curr, EXCP_NOCP,
                           syn_uncategorized(), s->fp_excp_el);
        return true;
    }

    return false;
}

static bool trans_NOCP_8_1(DisasContext *s, arg_nocp *a)
{
    /* This range needs a coprocessor check for v8.1M and later only */
    if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
        return false;
    }
    return trans_NOCP(s, a);
}