xref: /qemu/target/arm/tcg/tlb-insns.c (revision d6b6da1fc84173d1d8e8777c487c21ffeab5f5ce)
1 /*
2  * Helpers for TLBI insns
3  *
4  * This code is licensed under the GNU GPL v2 or later.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 #include "qemu/osdep.h"
9 #include "exec/exec-all.h"
10 #include "cpu.h"
11 #include "internals.h"
12 #include "cpu-features.h"
13 #include "cpregs.h"
14 
15 /* IS variants of TLB operations must affect all cores */
16 static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
17                              uint64_t value)
18 {
19     CPUState *cs = env_cpu(env);
20 
21     tlb_flush_all_cpus_synced(cs);
22 }
23 
24 static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
25                              uint64_t value)
26 {
27     CPUState *cs = env_cpu(env);
28 
29     tlb_flush_all_cpus_synced(cs);
30 }
31 
32 static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
33                              uint64_t value)
34 {
35     CPUState *cs = env_cpu(env);
36 
37     tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
38 }
39 
40 static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
41                              uint64_t value)
42 {
43     CPUState *cs = env_cpu(env);
44 
45     tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
46 }
47 
48 static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri,
49                           uint64_t value)
50 {
51     /* Invalidate all (TLBIALL) */
52     CPUState *cs = env_cpu(env);
53 
54     if (tlb_force_broadcast(env)) {
55         tlb_flush_all_cpus_synced(cs);
56     } else {
57         tlb_flush(cs);
58     }
59 }
60 
61 static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri,
62                           uint64_t value)
63 {
64     /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */
65     CPUState *cs = env_cpu(env);
66 
67     value &= TARGET_PAGE_MASK;
68     if (tlb_force_broadcast(env)) {
69         tlb_flush_page_all_cpus_synced(cs, value);
70     } else {
71         tlb_flush_page(cs, value);
72     }
73 }
74 
75 static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri,
76                            uint64_t value)
77 {
78     /* Invalidate by ASID (TLBIASID) */
79     CPUState *cs = env_cpu(env);
80 
81     if (tlb_force_broadcast(env)) {
82         tlb_flush_all_cpus_synced(cs);
83     } else {
84         tlb_flush(cs);
85     }
86 }
87 
88 static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
89                            uint64_t value)
90 {
91     /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */
92     CPUState *cs = env_cpu(env);
93 
94     value &= TARGET_PAGE_MASK;
95     if (tlb_force_broadcast(env)) {
96         tlb_flush_page_all_cpus_synced(cs, value);
97     } else {
98         tlb_flush_page(cs, value);
99     }
100 }
101 
102 static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
103                               uint64_t value)
104 {
105     CPUState *cs = env_cpu(env);
106     uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
107 
108     tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2);
109 }
110 
111 static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
112                                  uint64_t value)
113 {
114     CPUState *cs = env_cpu(env);
115     uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
116 
117     tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
118                                              ARMMMUIdxBit_E2);
119 }
120 
121 static void tlbiipas2_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
122                                 uint64_t value)
123 {
124     CPUState *cs = env_cpu(env);
125     uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12;
126 
127     tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2);
128 }
129 
130 static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
131                                 uint64_t value)
132 {
133     CPUState *cs = env_cpu(env);
134     uint64_t pageaddr = (value & MAKE_64BIT_MASK(0, 28)) << 12;
135 
136     tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, ARMMMUIdxBit_Stage2);
137 }
138 
139 static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
140                                uint64_t value)
141 {
142     CPUState *cs = env_cpu(env);
143 
144     tlb_flush_by_mmuidx(cs, alle1_tlbmask(env));
145 }
146 
147 static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
148                                   uint64_t value)
149 {
150     CPUState *cs = env_cpu(env);
151 
152     tlb_flush_by_mmuidx_all_cpus_synced(cs, alle1_tlbmask(env));
153 }
154 
155 
156 static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
157                               uint64_t value)
158 {
159     CPUState *cs = env_cpu(env);
160 
161     tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2);
162 }
163 
164 static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
165                                  uint64_t value)
166 {
167     CPUState *cs = env_cpu(env);
168 
169     tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2);
170 }
171 
172 static const ARMCPRegInfo tlbi_not_v7_cp_reginfo[] = {
173     /*
174      * MMU TLB control. Note that the wildcarding means we cover not just
175      * the unified TLB ops but also the dside/iside/inner-shareable variants.
176      */
177     { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY,
178       .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write,
179       .type = ARM_CP_NO_RAW },
180     { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY,
181       .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write,
182       .type = ARM_CP_NO_RAW },
183     { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY,
184       .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write,
185       .type = ARM_CP_NO_RAW },
186     { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY,
187       .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write,
188       .type = ARM_CP_NO_RAW },
189 };
190 
191 static const ARMCPRegInfo tlbi_v7_cp_reginfo[] = {
192     /* 32 bit ITLB invalidates */
193     { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0,
194       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
195       .writefn = tlbiall_write },
196     { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1,
197       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
198       .writefn = tlbimva_write },
199     { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2,
200       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
201       .writefn = tlbiasid_write },
202     /* 32 bit DTLB invalidates */
203     { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0,
204       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
205       .writefn = tlbiall_write },
206     { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1,
207       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
208       .writefn = tlbimva_write },
209     { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2,
210       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
211       .writefn = tlbiasid_write },
212     /* 32 bit TLB invalidates */
213     { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0,
214       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
215       .writefn = tlbiall_write },
216     { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1,
217       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
218       .writefn = tlbimva_write },
219     { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2,
220       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
221       .writefn = tlbiasid_write },
222     { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3,
223       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
224       .writefn = tlbimvaa_write },
225 };
226 
227 static const ARMCPRegInfo tlbi_v7mp_cp_reginfo[] = {
228     /* 32 bit TLB invalidates, Inner Shareable */
229     { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0,
230       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
231       .writefn = tlbiall_is_write },
232     { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1,
233       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
234       .writefn = tlbimva_is_write },
235     { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2,
236       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
237       .writefn = tlbiasid_is_write },
238     { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3,
239       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
240       .writefn = tlbimvaa_is_write },
241 };
242 
243 static const ARMCPRegInfo tlbi_v8_cp_reginfo[] = {
244     /* AArch32 TLB invalidate last level of translation table walk */
245     { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5,
246       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
247       .writefn = tlbimva_is_write },
248     { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7,
249       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlbis,
250       .writefn = tlbimvaa_is_write },
251     { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5,
252       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
253       .writefn = tlbimva_write },
254     { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7,
255       .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb,
256       .writefn = tlbimvaa_write },
257     { .name = "TLBIMVALH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 5,
258       .type = ARM_CP_NO_RAW, .access = PL2_W,
259       .writefn = tlbimva_hyp_write },
260     { .name = "TLBIMVALHIS",
261       .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 5,
262       .type = ARM_CP_NO_RAW, .access = PL2_W,
263       .writefn = tlbimva_hyp_is_write },
264     { .name = "TLBIIPAS2",
265       .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 1,
266       .type = ARM_CP_NO_RAW, .access = PL2_W,
267       .writefn = tlbiipas2_hyp_write },
268     { .name = "TLBIIPAS2IS",
269       .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 1,
270       .type = ARM_CP_NO_RAW, .access = PL2_W,
271       .writefn = tlbiipas2is_hyp_write },
272     { .name = "TLBIIPAS2L",
273       .cp = 15, .opc1 = 4, .crn = 8, .crm = 4, .opc2 = 5,
274       .type = ARM_CP_NO_RAW, .access = PL2_W,
275       .writefn = tlbiipas2_hyp_write },
276     { .name = "TLBIIPAS2LIS",
277       .cp = 15, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 5,
278       .type = ARM_CP_NO_RAW, .access = PL2_W,
279       .writefn = tlbiipas2is_hyp_write },
280 };
281 
282 static const ARMCPRegInfo tlbi_el2_cp_reginfo[] = {
283     { .name = "TLBIALLNSNH",
284       .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4,
285       .type = ARM_CP_NO_RAW, .access = PL2_W,
286       .writefn = tlbiall_nsnh_write },
287     { .name = "TLBIALLNSNHIS",
288       .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4,
289       .type = ARM_CP_NO_RAW, .access = PL2_W,
290       .writefn = tlbiall_nsnh_is_write },
291     { .name = "TLBIALLH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0,
292       .type = ARM_CP_NO_RAW, .access = PL2_W,
293       .writefn = tlbiall_hyp_write },
294     { .name = "TLBIALLHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 0,
295       .type = ARM_CP_NO_RAW, .access = PL2_W,
296       .writefn = tlbiall_hyp_is_write },
297     { .name = "TLBIMVAH", .cp = 15, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1,
298       .type = ARM_CP_NO_RAW, .access = PL2_W,
299       .writefn = tlbimva_hyp_write },
300     { .name = "TLBIMVAHIS", .cp = 15, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1,
301       .type = ARM_CP_NO_RAW, .access = PL2_W,
302       .writefn = tlbimva_hyp_is_write },
303 };
304 
305 void define_tlb_insn_regs(ARMCPU *cpu)
306 {
307     CPUARMState *env = &cpu->env;
308 
309     if (!arm_feature(env, ARM_FEATURE_V7)) {
310         define_arm_cp_regs(cpu, tlbi_not_v7_cp_reginfo);
311     } else {
312         define_arm_cp_regs(cpu, tlbi_v7_cp_reginfo);
313     }
314     if (arm_feature(env, ARM_FEATURE_V7MP) &&
315         !arm_feature(env, ARM_FEATURE_PMSA)) {
316         define_arm_cp_regs(cpu, tlbi_v7mp_cp_reginfo);
317     }
318     if (arm_feature(env, ARM_FEATURE_V8)) {
319         define_arm_cp_regs(cpu, tlbi_v8_cp_reginfo);
320     }
321     /*
322      * We retain the existing logic for when to register these TLBI
323      * ops (i.e. matching the condition for el2_cp_reginfo[] in
324      * helper.c), but we will be able to simplify this later.
325      */
326     if (arm_feature(env, ARM_FEATURE_EL2)
327         || (arm_feature(env, ARM_FEATURE_EL3)
328             && arm_feature(env, ARM_FEATURE_V8))) {
329         define_arm_cp_regs(cpu, tlbi_el2_cp_reginfo);
330     }
331 }
332