1/*
2 * Power ISA decode for Storage Control instructions
3 *
4 * Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * Store Control Instructions
22 */
23
24#include "mmu-book3s-v3.h"
25
26static bool trans_SLBIE(DisasContext *ctx, arg_SLBIE *a)
27{
28    REQUIRE_64BIT(ctx);
29    REQUIRE_INSNS_FLAGS(ctx, SLBI);
30    REQUIRE_SV(ctx);
31
32#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
33    gen_helper_SLBIE(tcg_env, cpu_gpr[a->rb]);
34#else
35    qemu_build_not_reached();
36#endif
37    return true;
38}
39
40static bool trans_SLBIEG(DisasContext *ctx, arg_SLBIEG *a)
41{
42    REQUIRE_64BIT(ctx);
43    REQUIRE_INSNS_FLAGS2(ctx, ISA300);
44    REQUIRE_SV(ctx);
45
46#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
47    gen_helper_SLBIEG(tcg_env, cpu_gpr[a->rb]);
48#else
49    qemu_build_not_reached();
50#endif
51    return true;
52}
53
54static bool trans_SLBIA(DisasContext *ctx, arg_SLBIA *a)
55{
56    REQUIRE_64BIT(ctx);
57    REQUIRE_INSNS_FLAGS(ctx, SLBI);
58    REQUIRE_SV(ctx);
59
60#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
61    gen_helper_SLBIA(tcg_env, tcg_constant_i32(a->ih));
62#else
63    qemu_build_not_reached();
64#endif
65    return true;
66}
67
68static bool trans_SLBIAG(DisasContext *ctx, arg_SLBIAG *a)
69{
70    REQUIRE_64BIT(ctx);
71    REQUIRE_INSNS_FLAGS2(ctx, ISA300);
72    REQUIRE_SV(ctx);
73
74#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
75    gen_helper_SLBIAG(tcg_env, cpu_gpr[a->rs], tcg_constant_i32(a->l));
76#else
77    qemu_build_not_reached();
78#endif
79    return true;
80}
81
82static bool trans_SLBMTE(DisasContext *ctx, arg_SLBMTE *a)
83{
84    REQUIRE_64BIT(ctx);
85    REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B);
86    REQUIRE_SV(ctx);
87
88#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
89    gen_helper_SLBMTE(tcg_env, cpu_gpr[a->rb], cpu_gpr[a->rt]);
90#else
91    qemu_build_not_reached();
92#endif
93    return true;
94}
95
96static bool trans_SLBMFEV(DisasContext *ctx, arg_SLBMFEV *a)
97{
98    REQUIRE_64BIT(ctx);
99    REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B);
100    REQUIRE_SV(ctx);
101
102#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
103    gen_helper_SLBMFEV(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]);
104#else
105    qemu_build_not_reached();
106#endif
107    return true;
108}
109
110static bool trans_SLBMFEE(DisasContext *ctx, arg_SLBMFEE *a)
111{
112    REQUIRE_64BIT(ctx);
113    REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B);
114    REQUIRE_SV(ctx);
115
116#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
117    gen_helper_SLBMFEE(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]);
118#else
119    qemu_build_not_reached();
120#endif
121    return true;
122}
123
124static bool trans_SLBFEE(DisasContext *ctx, arg_SLBFEE *a)
125{
126    REQUIRE_64BIT(ctx);
127    REQUIRE_INSNS_FLAGS(ctx, SEGMENT_64B);
128
129#if defined(CONFIG_USER_ONLY)
130    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
131#else
132
133#if defined(TARGET_PPC64)
134    TCGLabel *l1, *l2;
135
136    if (unlikely(ctx->pr)) {
137        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
138        return true;
139    }
140    gen_helper_SLBFEE(cpu_gpr[a->rt], tcg_env,
141                             cpu_gpr[a->rb]);
142    l1 = gen_new_label();
143    l2 = gen_new_label();
144    tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
145    tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], -1, l1);
146    tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
147    tcg_gen_br(l2);
148    gen_set_label(l1);
149    tcg_gen_movi_tl(cpu_gpr[a->rt], 0);
150    gen_set_label(l2);
151#else
152    qemu_build_not_reached();
153#endif
154#endif
155    return true;
156}
157
158static bool trans_SLBSYNC(DisasContext *ctx, arg_SLBSYNC *a)
159{
160    REQUIRE_64BIT(ctx);
161    REQUIRE_INSNS_FLAGS2(ctx, ISA300);
162    REQUIRE_SV(ctx);
163
164#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
165    gen_check_tlb_flush(ctx, true);
166#else
167    qemu_build_not_reached();
168#endif
169    return true;
170}
171
172static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local)
173{
174#if defined(CONFIG_USER_ONLY)
175    gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
176    return true;
177#else
178    TCGv_i32 t1;
179    int rb;
180
181    rb = a->rb;
182
183    if ((ctx->insns_flags2 & PPC2_ISA300) == 0) {
184        /*
185         * Before Power ISA 3.0, the corresponding bits of RIC, PRS, and R
186         * (and RS for tlbiel) were reserved fields and should be ignored.
187         */
188        a->ric = 0;
189        a->prs = false;
190        a->r = false;
191        if (local) {
192            a->rs = 0;
193        }
194    }
195
196    if (ctx->pr) {
197        /* tlbie[l] is privileged... */
198        gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
199        return true;
200    } else if (!ctx->hv) {
201        if ((!a->prs && ctx->hr) || (!local && !ctx->gtse)) {
202            /*
203             * ... except when PRS=0 and HR=1, or when GTSE=0 for tlbie,
204             * making it hypervisor privileged.
205             */
206            gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC);
207            return true;
208        }
209    }
210
211    if (!local && NARROW_MODE(ctx)) {
212        TCGv t0 = tcg_temp_new();
213        tcg_gen_ext32u_tl(t0, cpu_gpr[rb]);
214        gen_helper_tlbie(tcg_env, t0);
215
216#if defined(TARGET_PPC64)
217    /*
218     * ISA 3.1B says that MSR SF must be 1 when this instruction is executed;
219     * otherwise the results are undefined.
220     */
221    } else if (a->r) {
222        gen_helper_tlbie_isa300(tcg_env, cpu_gpr[rb], cpu_gpr[a->rs],
223                tcg_constant_i32(a->ric << TLBIE_F_RIC_SHIFT |
224                                 a->prs << TLBIE_F_PRS_SHIFT |
225                                 a->r << TLBIE_F_R_SHIFT |
226                                 local << TLBIE_F_LOCAL_SHIFT));
227        if (!local) {
228            /*
229             * Global TLB flush uses async-work which must run before the
230             * next instruction, so this must be the last in the TB.
231             */
232            ctx->base.is_jmp = DISAS_EXIT_UPDATE;
233        }
234        return true;
235#endif
236
237    } else {
238        gen_helper_tlbie(tcg_env, cpu_gpr[rb]);
239    }
240
241    if (local) {
242        return true;
243    }
244
245    t1 = tcg_temp_new_i32();
246    tcg_gen_ld_i32(t1, tcg_env, offsetof(CPUPPCState, tlb_need_flush));
247    tcg_gen_ori_i32(t1, t1, TLB_NEED_GLOBAL_FLUSH);
248    tcg_gen_st_i32(t1, tcg_env, offsetof(CPUPPCState, tlb_need_flush));
249
250    return true;
251#endif
252}
253
254TRANS_FLAGS(MEM_TLBIE, TLBIE, do_tlbie, false)
255TRANS_FLAGS(MEM_TLBIE, TLBIEL, do_tlbie, true)
256