| 1 | /* |
| 2 | * System instructions for address translation |
| 3 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | */ |
| 5 | |
| 6 | #include "qemu/osdep.h" |
| 7 | #include "exec/page-protection.h" |
| 8 | #include "cpu.h" |
| 9 | #include "cpu-features.h" |
| 10 | #include "internals.h" |
| 11 | #include "cpregs.h" |
| 12 | |
| 13 | |
| 14 | static int par_el1_shareability(GetPhysAddrResult *res) |
| 15 | { |
| 16 | /* |
| 17 | * The PAR_EL1.SH field must be 0b10 for Device or Normal-NC |
| 18 | * memory -- see pseudocode PAREncodeShareability(). |
| 19 | */ |
| 20 | if (((res->cacheattrs.attrs & 0xf0) == 0) || |
| 21 | res->cacheattrs.attrs == 0x44 || res->cacheattrs.attrs == 0x40) { |
| 22 | return 2; |
| 23 | } |
| 24 | return res->cacheattrs.shareability; |
| 25 | } |
| 26 | |
| 27 | static uint64_t do_ats_write(CPUARMState *env, uint64_t value, |
| 28 | unsigned prot_check, ARMMMUIdx mmu_idx, |
| 29 | ARMSecuritySpace ss) |
| 30 | { |
| 31 | uint64_t par64; |
| 32 | bool format64 = false; |
| 33 | ARMMMUFaultInfo fi = {}; |
| 34 | GetPhysAddrResult res = {}; |
| 35 | bool ok = get_phys_addr_for_at(env, value, prot_check, |
| 36 | mmu_idx, ss, &res, &fi); |
| 37 | |
| 38 | /* |
| 39 | * ATS operations only do S1 or S1+S2 translations, so we never |
| 40 | * have to deal with the ARMCacheAttrs format for S2 only. |
| 41 | * (Note that res fields are only valid on ptw success.) |
| 42 | */ |
| 43 | assert(!ok || !res.cacheattrs.is_s2_format); |
| 44 | |
| 45 | if (!ok) { |
| 46 | /* |
| 47 | * Some kinds of translation fault must cause exceptions rather |
| 48 | * than being reported in the PAR. |
| 49 | */ |
| 50 | int current_el = arm_current_el(env); |
| 51 | int target_el; |
| 52 | uint32_t syn, fsr, fsc; |
| 53 | bool take_exc = false; |
| 54 | |
| 55 | if (fi.s1ptw && current_el == 1 |
| 56 | && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { |
| 57 | /* |
| 58 | * Synchronous stage 2 fault on an access made as part of the |
| 59 | * translation table walk for AT S1E0* or AT S1E1* insn |
| 60 | * executed from NS EL1. If this is a synchronous external abort |
| 61 | * and SCR_EL3.EA == 1, then we take a synchronous external abort |
| 62 | * to EL3. Otherwise the fault is taken as an exception to EL2, |
| 63 | * and HPFAR_EL2 holds the faulting IPA. |
| 64 | */ |
| 65 | if (fi.type == ARMFault_SyncExternalOnWalk && |
| 66 | (env->cp15.scr_el3 & SCR_EA)) { |
| 67 | target_el = 3; |
| 68 | } else { |
| 69 | env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4; |
| 70 | if (arm_is_secure_below_el3(env) && fi.s1ns) { |
| 71 | env->cp15.hpfar_el2 |= HPFAR_NS; |
| 72 | } |
| 73 | target_el = 2; |
| 74 | } |
| 75 | take_exc = true; |
| 76 | } else if (fi.type == ARMFault_SyncExternalOnWalk) { |
| 77 | /* |
| 78 | * Synchronous external aborts during a translation table walk |
| 79 | * are taken as Data Abort exceptions. |
| 80 | */ |
| 81 | if (fi.stage2) { |
| 82 | if (current_el == 3) { |
| 83 | target_el = 3; |
| 84 | } else { |
| 85 | target_el = 2; |
| 86 | } |
| 87 | } else { |
| 88 | target_el = exception_target_el(env); |
| 89 | } |
| 90 | take_exc = true; |
| 91 | } |
| 92 | |
| 93 | if (take_exc) { |
| 94 | /* Construct FSR and FSC using same logic as arm_deliver_fault() */ |
| 95 | if (target_el == 2 || arm_el_is_aa64(env, target_el) || |
| 96 | arm_s1_regime_using_lpae_format(env, mmu_idx)) { |
| 97 | fsr = arm_fi_to_lfsc(&fi); |
| 98 | fsc = extract32(fsr, 0, 6); |
| 99 | } else { |
| 100 | fsr = arm_fi_to_sfsc(&fi); |
| 101 | fsc = 0x3f; |
| 102 | } |
| 103 | /* |
| 104 | * Report exception with ESR indicating a fault due to a |
| 105 | * translation table walk for a cache maintenance instruction. |
| 106 | */ |
| 107 | syn = syn_data_abort_no_iss(current_el == target_el, 0, |
| 108 | fi.ea, 1, fi.s1ptw, 1, fsc); |
| 109 | env->exception.vaddress = value; |
| 110 | env->exception.fsr = fsr; |
| 111 | raise_exception(env, EXCP_DATA_ABORT, syn, target_el); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if (is_a64(env)) { |
| 116 | format64 = true; |
| 117 | } else if (arm_feature(env, ARM_FEATURE_LPAE)) { |
| 118 | /* |
| 119 | * ATS1Cxx: |
| 120 | * * TTBCR.EAE determines whether the result is returned using the |
| 121 | * 32-bit or the 64-bit PAR format |
| 122 | * * Instructions executed in Hyp mode always use the 64bit format |
| 123 | * |
| 124 | * ATS1S2NSOxx uses the 64bit format if any of the following is true: |
| 125 | * * The Non-secure TTBCR.EAE bit is set to 1 |
| 126 | * * The implementation includes EL2, and the value of HCR.VM is 1 |
| 127 | * |
| 128 | * (Note that HCR.DC makes HCR.VM behave as if it is 1.) |
| 129 | * |
| 130 | * ATS1Hx always uses the 64bit format. |
| 131 | */ |
| 132 | format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); |
| 133 | |
| 134 | if (arm_feature(env, ARM_FEATURE_EL2)) { |
| 135 | if (mmu_idx == ARMMMUIdx_E10_0 || |
| 136 | mmu_idx == ARMMMUIdx_E10_1 || |
| 137 | mmu_idx == ARMMMUIdx_E10_1_PAN) { |
| 138 | format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); |
| 139 | } else { |
| 140 | format64 |= arm_current_el(env) == 2; |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | if (format64) { |
| 146 | /* Create a 64-bit PAR */ |
| 147 | par64 = (1 << 11); /* LPAE bit always set */ |
| 148 | if (ok) { |
| 149 | par64 |= res.f.phys_addr & ~0xfffULL; |
| 150 | if (!res.f.attrs.secure) { |
| 151 | par64 |= (1 << 9); /* NS */ |
| 152 | } |
| 153 | par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */ |
| 154 | par64 |= par_el1_shareability(&res) << 7; /* SH */ |
| 155 | } else { |
| 156 | uint32_t fsr = arm_fi_to_lfsc(&fi); |
| 157 | |
| 158 | par64 |= 1; /* F */ |
| 159 | par64 |= (fsr & 0x3f) << 1; /* FS */ |
| 160 | if (fi.stage2) { |
| 161 | par64 |= (1 << 9); /* S */ |
| 162 | } |
| 163 | if (fi.s1ptw) { |
| 164 | par64 |= (1 << 8); /* PTW */ |
| 165 | } |
| 166 | } |
| 167 | } else { |
| 168 | /* |
| 169 | * fsr is a DFSR/IFSR value for the short descriptor |
| 170 | * translation table format (with WnR always clear). |
| 171 | * Convert it to a 32-bit PAR. |
| 172 | */ |
| 173 | if (ok) { |
| 174 | /* We do not set any attribute bits in the PAR */ |
| 175 | if (res.f.lg_page_size == 24 |
| 176 | && arm_feature(env, ARM_FEATURE_V7)) { |
| 177 | par64 = (res.f.phys_addr & 0xff000000) | (1 << 1); |
| 178 | } else { |
| 179 | par64 = res.f.phys_addr & 0xfffff000; |
| 180 | } |
| 181 | if (!res.f.attrs.secure) { |
| 182 | par64 |= (1 << 9); /* NS */ |
| 183 | } |
| 184 | } else { |
| 185 | uint32_t fsr = arm_fi_to_sfsc(&fi); |
| 186 | |
| 187 | par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) | |
| 188 | ((fsr & 0xf) << 1) | 1; |
| 189 | } |
| 190 | } |
| 191 | return par64; |
| 192 | } |
| 193 | |
| 194 | static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| 195 | { |
| 196 | unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; |
| 197 | uint64_t par64; |
| 198 | ARMMMUIdx mmu_idx; |
| 199 | int el = arm_current_el(env); |
| 200 | ARMSecuritySpace ss = arm_security_space(env); |
| 201 | |
| 202 | switch (ri->opc2 & 6) { |
| 203 | case 0: |
| 204 | /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ |
| 205 | switch (el) { |
| 206 | case 3: |
| 207 | if (ri->crm == 9 && arm_pan_enabled(env)) { |
| 208 | mmu_idx = ARMMMUIdx_E30_3_PAN; |
| 209 | } else { |
| 210 | mmu_idx = ARMMMUIdx_E3; |
| 211 | } |
| 212 | break; |
| 213 | case 2: |
| 214 | g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ |
| 215 | /* fall through */ |
| 216 | case 1: |
| 217 | if (ri->crm == 9 && arm_pan_enabled(env)) { |
| 218 | mmu_idx = ARMMMUIdx_Stage1_E1_PAN; |
| 219 | } else { |
| 220 | mmu_idx = ARMMMUIdx_Stage1_E1; |
| 221 | } |
| 222 | break; |
| 223 | default: |
| 224 | g_assert_not_reached(); |
| 225 | } |
| 226 | break; |
| 227 | case 2: |
| 228 | /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ |
| 229 | switch (el) { |
| 230 | case 3: |
| 231 | mmu_idx = ARMMMUIdx_E30_0; |
| 232 | break; |
| 233 | case 2: |
| 234 | g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ |
| 235 | mmu_idx = ARMMMUIdx_Stage1_E0; |
| 236 | break; |
| 237 | case 1: |
| 238 | mmu_idx = ARMMMUIdx_Stage1_E0; |
| 239 | break; |
| 240 | default: |
| 241 | g_assert_not_reached(); |
| 242 | } |
| 243 | break; |
| 244 | case 4: |
| 245 | /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ |
| 246 | mmu_idx = ARMMMUIdx_E10_1; |
| 247 | ss = ARMSS_NonSecure; |
| 248 | break; |
| 249 | case 6: |
| 250 | /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ |
| 251 | mmu_idx = ARMMMUIdx_E10_0; |
| 252 | ss = ARMSS_NonSecure; |
| 253 | break; |
| 254 | default: |
| 255 | g_assert_not_reached(); |
| 256 | } |
| 257 | |
| 258 | par64 = do_ats_write(env, value, access_perm, mmu_idx, ss); |
| 259 | |
| 260 | A32_BANKED_CURRENT_REG_SET(env, par, par64); |
| 261 | } |
| 262 | |
| 263 | static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| 264 | uint64_t value) |
| 265 | { |
| 266 | unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; |
| 267 | uint64_t par64; |
| 268 | |
| 269 | /* There is no SecureEL2 for AArch32. */ |
| 270 | par64 = do_ats_write(env, value, access_perm, ARMMMUIdx_E2, |
| 271 | ARMSS_NonSecure); |
| 272 | |
| 273 | A32_BANKED_CURRENT_REG_SET(env, par, par64); |
| 274 | } |
| 275 | |
| 276 | static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| 277 | bool isread) |
| 278 | { |
| 279 | /* |
| 280 | * R_NYXTL: instruction is UNDEFINED if it applies to an Exception level |
| 281 | * lower than EL3 and the combination SCR_EL3.{NSE,NS} is reserved. This can |
| 282 | * only happen when executing at EL3 because that combination also causes an |
| 283 | * illegal exception return. We don't need to check FEAT_RME either, because |
| 284 | * scr_write() ensures that the NSE bit is not set otherwise. |
| 285 | */ |
| 286 | if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { |
| 287 | return CP_ACCESS_UNDEFINED; |
| 288 | } |
| 289 | return CP_ACCESS_OK; |
| 290 | } |
| 291 | |
| 292 | static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| 293 | bool isread) |
| 294 | { |
| 295 | if (arm_current_el(env) == 3 && |
| 296 | !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { |
| 297 | return CP_ACCESS_UNDEFINED; |
| 298 | } |
| 299 | return at_e012_access(env, ri, isread); |
| 300 | } |
| 301 | |
| 302 | static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| 303 | bool isread) |
| 304 | { |
| 305 | if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) { |
| 306 | return CP_ACCESS_TRAP_EL2; |
| 307 | } |
| 308 | return at_e012_access(env, ri, isread); |
| 309 | } |
| 310 | |
| 311 | static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, |
| 312 | uint64_t value) |
| 313 | { |
| 314 | unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; |
| 315 | ARMMMUIdx mmu_idx; |
| 316 | uint64_t hcr_el2 = arm_hcr_el2_eff(env); |
| 317 | bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); |
| 318 | bool for_el3 = false; |
| 319 | ARMSecuritySpace ss; |
| 320 | |
| 321 | switch (ri->opc2 & 6) { |
| 322 | case 0: |
| 323 | switch (ri->opc1) { |
| 324 | case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ |
| 325 | if (ri->crm == 9 && arm_pan_enabled(env)) { |
| 326 | mmu_idx = regime_e20 ? |
| 327 | ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; |
| 328 | } else { |
| 329 | mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; |
| 330 | } |
| 331 | break; |
| 332 | case 4: /* AT S1E2R, AT S1E2W */ |
| 333 | mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; |
| 334 | break; |
| 335 | case 6: /* AT S1E3R, AT S1E3W */ |
| 336 | mmu_idx = ARMMMUIdx_E3; |
| 337 | for_el3 = true; |
| 338 | break; |
| 339 | default: |
| 340 | g_assert_not_reached(); |
| 341 | } |
| 342 | break; |
| 343 | case 2: /* AT S1E0R, AT S1E0W */ |
| 344 | mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_Stage1_E0; |
| 345 | break; |
| 346 | case 4: /* AT S12E1R, AT S12E1W */ |
| 347 | mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E10_1; |
| 348 | break; |
| 349 | case 6: /* AT S12E0R, AT S12E0W */ |
| 350 | mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_E10_0; |
| 351 | break; |
| 352 | default: |
| 353 | g_assert_not_reached(); |
| 354 | } |
| 355 | |
| 356 | ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); |
| 357 | env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss); |
| 358 | } |
| 359 | |
| 360 | static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| 361 | bool isread) |
| 362 | { |
| 363 | if (ri->opc2 & 4) { |
| 364 | /* |
| 365 | * The ATS12NSO* operations must trap to EL3 or EL2 if executed in |
| 366 | * Secure EL1 (which can only happen if EL3 is AArch64). |
| 367 | * They are simply UNDEF if executed from NS EL1. |
| 368 | * They function normally from EL2 or EL3. |
| 369 | */ |
| 370 | if (arm_current_el(env) == 1) { |
| 371 | if (arm_is_secure_below_el3(env)) { |
| 372 | if (env->cp15.scr_el3 & SCR_EEL2) { |
| 373 | return CP_ACCESS_TRAP_EL2; |
| 374 | } |
| 375 | return CP_ACCESS_TRAP_EL3; |
| 376 | } |
| 377 | return CP_ACCESS_UNDEFINED; |
| 378 | } |
| 379 | } |
| 380 | return CP_ACCESS_OK; |
| 381 | } |
| 382 | |
| 383 | static const ARMCPRegInfo vapa_ats_reginfo[] = { |
| 384 | /* This underdecoding is safe because the reginfo is NO_RAW. */ |
| 385 | { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, |
| 386 | .access = PL1_W, .accessfn = ats_access, |
| 387 | .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, |
| 388 | }; |
| 389 | |
| 390 | static const ARMCPRegInfo v8_ats_reginfo[] = { |
| 391 | /* 64 bit address translation operations */ |
| 392 | { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, |
| 393 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, |
| 394 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 395 | .fgt = FGT_ATS1E1R, |
| 396 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 397 | { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, |
| 398 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, |
| 399 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 400 | .fgt = FGT_ATS1E1W, |
| 401 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 402 | { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, |
| 403 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, |
| 404 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 405 | .fgt = FGT_ATS1E0R, |
| 406 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 407 | { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, |
| 408 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, |
| 409 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 410 | .fgt = FGT_ATS1E0W, |
| 411 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 412 | { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, |
| 413 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, |
| 414 | .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 415 | .accessfn = at_e012_access, .writefn = ats_write64 }, |
| 416 | { .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64, |
| 417 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5, |
| 418 | .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 419 | .accessfn = at_e012_access, .writefn = ats_write64 }, |
| 420 | { .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64, |
| 421 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6, |
| 422 | .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 423 | .accessfn = at_e012_access, .writefn = ats_write64 }, |
| 424 | { .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64, |
| 425 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7, |
| 426 | .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 427 | .accessfn = at_e012_access, .writefn = ats_write64 }, |
| 428 | /* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */ |
| 429 | { .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64, |
| 430 | .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0, |
| 431 | .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 432 | .writefn = ats_write64 }, |
| 433 | { .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64, |
| 434 | .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1, |
| 435 | .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 436 | .writefn = ats_write64 }, |
| 437 | }; |
| 438 | |
| 439 | static const ARMCPRegInfo el2_ats_reginfo[] = { |
| 440 | /* |
| 441 | * Unlike the other EL2-related AT operations, these must |
| 442 | * UNDEF from EL3 if EL2 is not implemented, which is why we |
| 443 | * define them here rather than with the rest of the AT ops. |
| 444 | */ |
| 445 | { .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64, |
| 446 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, |
| 447 | .access = PL2_W, .accessfn = at_s1e2_access, |
| 448 | .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, |
| 449 | .writefn = ats_write64 }, |
| 450 | { .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64, |
| 451 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, |
| 452 | .access = PL2_W, .accessfn = at_s1e2_access, |
| 453 | .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, |
| 454 | .writefn = ats_write64 }, |
| 455 | /* |
| 456 | * The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE |
| 457 | * if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3 |
| 458 | * with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose |
| 459 | * to behave as if SCR.NS was 1. |
| 460 | */ |
| 461 | { .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, |
| 462 | .access = PL2_W, |
| 463 | .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, |
| 464 | { .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, |
| 465 | .access = PL2_W, |
| 466 | .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, |
| 467 | }; |
| 468 | |
| 469 | static const ARMCPRegInfo ats1e1_reginfo[] = { |
| 470 | { .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64, |
| 471 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, |
| 472 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 473 | .fgt = FGT_ATS1E1RP, |
| 474 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 475 | { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, |
| 476 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, |
| 477 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 478 | .fgt = FGT_ATS1E1WP, |
| 479 | .accessfn = at_s1e01_access, .writefn = ats_write64 }, |
| 480 | }; |
| 481 | |
| 482 | static const ARMCPRegInfo ats1cp_reginfo[] = { |
| 483 | { .name = "ATS1CPRP", |
| 484 | .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, |
| 485 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 486 | .writefn = ats_write }, |
| 487 | { .name = "ATS1CPWP", |
| 488 | .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, |
| 489 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 490 | .writefn = ats_write }, |
| 491 | }; |
| 492 | |
| 493 | static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| 494 | { |
| 495 | uint64_t hcr_el2 = arm_hcr_el2_eff(env); |
| 496 | bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); |
| 497 | ARMMMUIdx mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; |
| 498 | ARMSecuritySpace ss = arm_security_space_below_el3(env); |
| 499 | |
| 500 | env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); |
| 501 | } |
| 502 | |
| 503 | static void ats_s1e2a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| 504 | { |
| 505 | uint64_t hcr_el2 = arm_hcr_el2_eff(env); |
| 506 | ARMMMUIdx mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; |
| 507 | ARMSecuritySpace ss = arm_security_space_below_el3(env); |
| 508 | |
| 509 | env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); |
| 510 | } |
| 511 | |
| 512 | static void ats_s1e3a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| 513 | { |
| 514 | env->cp15.par_el[1] = do_ats_write(env, value, 0, ARMMMUIdx_E3, |
| 515 | arm_security_space(env)); |
| 516 | } |
| 517 | |
| 518 | static const ARMCPRegInfo ats1a_reginfo[] = { |
| 519 | { .name = "AT_S1E1A", .state = ARM_CP_STATE_AA64, |
| 520 | .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 2, |
| 521 | .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 522 | .fgt = FGT_ATS1E1A, |
| 523 | .accessfn = at_s1e01_access, .writefn = ats_s1e1a }, |
| 524 | { .name = "AT_S1E2A", .state = ARM_CP_STATE_AA64, |
| 525 | .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 9, .opc2 = 2, |
| 526 | .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 527 | .accessfn = at_s1e2_access, .writefn = ats_s1e2a }, |
| 528 | { .name = "AT_S1E3A", .state = ARM_CP_STATE_AA64, |
| 529 | .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 9, .opc2 = 2, |
| 530 | .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, |
| 531 | .writefn = ats_s1e3a }, |
| 532 | }; |
| 533 | |
| 534 | void define_at_insn_regs(ARMCPU *cpu) |
| 535 | { |
| 536 | CPUARMState *env = &cpu->env; |
| 537 | |
| 538 | if (arm_feature(env, ARM_FEATURE_VAPA)) { |
| 539 | define_arm_cp_regs(cpu, vapa_ats_reginfo); |
| 540 | } |
| 541 | if (arm_feature(env, ARM_FEATURE_V8)) { |
| 542 | define_arm_cp_regs(cpu, v8_ats_reginfo); |
| 543 | } |
| 544 | if (arm_feature(env, ARM_FEATURE_EL2) |
| 545 | || (arm_feature(env, ARM_FEATURE_EL3) |
| 546 | && arm_feature(env, ARM_FEATURE_V8))) { |
| 547 | define_arm_cp_regs(cpu, el2_ats_reginfo); |
| 548 | } |
| 549 | if (cpu_isar_feature(aa64_ats1e1, cpu)) { |
| 550 | define_arm_cp_regs(cpu, ats1e1_reginfo); |
| 551 | } |
| 552 | if (cpu_isar_feature(aa32_ats1e1, cpu)) { |
| 553 | define_arm_cp_regs(cpu, ats1cp_reginfo); |
| 554 | } |
| 555 | if (cpu_isar_feature(aa64_ats1a, cpu)) { |
| 556 | define_arm_cp_regs(cpu, ats1a_reginfo); |
| 557 | } |
| 558 | } |