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