mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
19 KiB
C
520 lines
19 KiB
C
/*
|
|
* System instructions for address translation
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "cpu.h"
|
|
#include "cpu-features.h"
|
|
#include "internals.h"
|
|
#include "cpregs.h"
|
|
|
|
|
|
static int par_el1_shareability(GetPhysAddrResult *res)
|
|
{
|
|
/*
|
|
* The PAR_EL1.SH field must be 0b10 for Device or Normal-NC
|
|
* memory -- see pseudocode PAREncodeShareability().
|
|
*/
|
|
if (((res->cacheattrs.attrs & 0xf0) == 0) ||
|
|
res->cacheattrs.attrs == 0x44 || res->cacheattrs.attrs == 0x40) {
|
|
return 2;
|
|
}
|
|
return res->cacheattrs.shareability;
|
|
}
|
|
|
|
static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
|
|
MMUAccessType access_type, ARMMMUIdx mmu_idx,
|
|
ARMSecuritySpace ss)
|
|
{
|
|
bool ret;
|
|
uint64_t par64;
|
|
bool format64 = false;
|
|
ARMMMUFaultInfo fi = {};
|
|
GetPhysAddrResult res = {};
|
|
|
|
/*
|
|
* I_MXTJT: Granule protection checks are not performed on the final
|
|
* address of a successful translation. This is a translation not a
|
|
* memory reference, so "memop = none = 0".
|
|
*/
|
|
ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0,
|
|
mmu_idx, ss, &res, &fi);
|
|
|
|
/*
|
|
* ATS operations only do S1 or S1+S2 translations, so we never
|
|
* have to deal with the ARMCacheAttrs format for S2 only.
|
|
*/
|
|
assert(!res.cacheattrs.is_s2_format);
|
|
|
|
if (ret) {
|
|
/*
|
|
* Some kinds of translation fault must cause exceptions rather
|
|
* than being reported in the PAR.
|
|
*/
|
|
int current_el = arm_current_el(env);
|
|
int target_el;
|
|
uint32_t syn, fsr, fsc;
|
|
bool take_exc = false;
|
|
|
|
if (fi.s1ptw && current_el == 1
|
|
&& arm_mmu_idx_is_stage1_of_2(mmu_idx)) {
|
|
/*
|
|
* Synchronous stage 2 fault on an access made as part of the
|
|
* translation table walk for AT S1E0* or AT S1E1* insn
|
|
* executed from NS EL1. If this is a synchronous external abort
|
|
* and SCR_EL3.EA == 1, then we take a synchronous external abort
|
|
* to EL3. Otherwise the fault is taken as an exception to EL2,
|
|
* and HPFAR_EL2 holds the faulting IPA.
|
|
*/
|
|
if (fi.type == ARMFault_SyncExternalOnWalk &&
|
|
(env->cp15.scr_el3 & SCR_EA)) {
|
|
target_el = 3;
|
|
} else {
|
|
env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4;
|
|
if (arm_is_secure_below_el3(env) && fi.s1ns) {
|
|
env->cp15.hpfar_el2 |= HPFAR_NS;
|
|
}
|
|
target_el = 2;
|
|
}
|
|
take_exc = true;
|
|
} else if (fi.type == ARMFault_SyncExternalOnWalk) {
|
|
/*
|
|
* Synchronous external aborts during a translation table walk
|
|
* are taken as Data Abort exceptions.
|
|
*/
|
|
if (fi.stage2) {
|
|
if (current_el == 3) {
|
|
target_el = 3;
|
|
} else {
|
|
target_el = 2;
|
|
}
|
|
} else {
|
|
target_el = exception_target_el(env);
|
|
}
|
|
take_exc = true;
|
|
}
|
|
|
|
if (take_exc) {
|
|
/* Construct FSR and FSC using same logic as arm_deliver_fault() */
|
|
if (target_el == 2 || arm_el_is_aa64(env, target_el) ||
|
|
arm_s1_regime_using_lpae_format(env, mmu_idx)) {
|
|
fsr = arm_fi_to_lfsc(&fi);
|
|
fsc = extract32(fsr, 0, 6);
|
|
} else {
|
|
fsr = arm_fi_to_sfsc(&fi);
|
|
fsc = 0x3f;
|
|
}
|
|
/*
|
|
* Report exception with ESR indicating a fault due to a
|
|
* translation table walk for a cache maintenance instruction.
|
|
*/
|
|
syn = syn_data_abort_no_iss(current_el == target_el, 0,
|
|
fi.ea, 1, fi.s1ptw, 1, fsc);
|
|
env->exception.vaddress = value;
|
|
env->exception.fsr = fsr;
|
|
raise_exception(env, EXCP_DATA_ABORT, syn, target_el);
|
|
}
|
|
}
|
|
|
|
if (is_a64(env)) {
|
|
format64 = true;
|
|
} else if (arm_feature(env, ARM_FEATURE_LPAE)) {
|
|
/*
|
|
* ATS1Cxx:
|
|
* * TTBCR.EAE determines whether the result is returned using the
|
|
* 32-bit or the 64-bit PAR format
|
|
* * Instructions executed in Hyp mode always use the 64bit format
|
|
*
|
|
* ATS1S2NSOxx uses the 64bit format if any of the following is true:
|
|
* * The Non-secure TTBCR.EAE bit is set to 1
|
|
* * The implementation includes EL2, and the value of HCR.VM is 1
|
|
*
|
|
* (Note that HCR.DC makes HCR.VM behave as if it is 1.)
|
|
*
|
|
* ATS1Hx always uses the 64bit format.
|
|
*/
|
|
format64 = arm_s1_regime_using_lpae_format(env, mmu_idx);
|
|
|
|
if (arm_feature(env, ARM_FEATURE_EL2)) {
|
|
if (mmu_idx == ARMMMUIdx_E10_0 ||
|
|
mmu_idx == ARMMMUIdx_E10_1 ||
|
|
mmu_idx == ARMMMUIdx_E10_1_PAN) {
|
|
format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC);
|
|
} else {
|
|
format64 |= arm_current_el(env) == 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (format64) {
|
|
/* Create a 64-bit PAR */
|
|
par64 = (1 << 11); /* LPAE bit always set */
|
|
if (!ret) {
|
|
par64 |= res.f.phys_addr & ~0xfffULL;
|
|
if (!res.f.attrs.secure) {
|
|
par64 |= (1 << 9); /* NS */
|
|
}
|
|
par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */
|
|
par64 |= par_el1_shareability(&res) << 7; /* SH */
|
|
} else {
|
|
uint32_t fsr = arm_fi_to_lfsc(&fi);
|
|
|
|
par64 |= 1; /* F */
|
|
par64 |= (fsr & 0x3f) << 1; /* FS */
|
|
if (fi.stage2) {
|
|
par64 |= (1 << 9); /* S */
|
|
}
|
|
if (fi.s1ptw) {
|
|
par64 |= (1 << 8); /* PTW */
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* fsr is a DFSR/IFSR value for the short descriptor
|
|
* translation table format (with WnR always clear).
|
|
* Convert it to a 32-bit PAR.
|
|
*/
|
|
if (!ret) {
|
|
/* We do not set any attribute bits in the PAR */
|
|
if (res.f.lg_page_size == 24
|
|
&& arm_feature(env, ARM_FEATURE_V7)) {
|
|
par64 = (res.f.phys_addr & 0xff000000) | (1 << 1);
|
|
} else {
|
|
par64 = res.f.phys_addr & 0xfffff000;
|
|
}
|
|
if (!res.f.attrs.secure) {
|
|
par64 |= (1 << 9); /* NS */
|
|
}
|
|
} else {
|
|
uint32_t fsr = arm_fi_to_sfsc(&fi);
|
|
|
|
par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) |
|
|
((fsr & 0xf) << 1) | 1;
|
|
}
|
|
}
|
|
return par64;
|
|
}
|
|
|
|
static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
|
{
|
|
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
|
uint64_t par64;
|
|
ARMMMUIdx mmu_idx;
|
|
int el = arm_current_el(env);
|
|
ARMSecuritySpace ss = arm_security_space(env);
|
|
|
|
switch (ri->opc2 & 6) {
|
|
case 0:
|
|
/* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */
|
|
switch (el) {
|
|
case 3:
|
|
if (ri->crm == 9 && arm_pan_enabled(env)) {
|
|
mmu_idx = ARMMMUIdx_E30_3_PAN;
|
|
} else {
|
|
mmu_idx = ARMMMUIdx_E3;
|
|
}
|
|
break;
|
|
case 2:
|
|
g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */
|
|
/* fall through */
|
|
case 1:
|
|
if (ri->crm == 9 && arm_pan_enabled(env)) {
|
|
mmu_idx = ARMMMUIdx_Stage1_E1_PAN;
|
|
} else {
|
|
mmu_idx = ARMMMUIdx_Stage1_E1;
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
break;
|
|
case 2:
|
|
/* stage 1 current state PL0: ATS1CUR, ATS1CUW */
|
|
switch (el) {
|
|
case 3:
|
|
mmu_idx = ARMMMUIdx_E30_0;
|
|
break;
|
|
case 2:
|
|
g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */
|
|
mmu_idx = ARMMMUIdx_Stage1_E0;
|
|
break;
|
|
case 1:
|
|
mmu_idx = ARMMMUIdx_Stage1_E0;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
break;
|
|
case 4:
|
|
/* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */
|
|
mmu_idx = ARMMMUIdx_E10_1;
|
|
ss = ARMSS_NonSecure;
|
|
break;
|
|
case 6:
|
|
/* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */
|
|
mmu_idx = ARMMMUIdx_E10_0;
|
|
ss = ARMSS_NonSecure;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
par64 = do_ats_write(env, value, access_type, mmu_idx, ss);
|
|
|
|
A32_BANKED_CURRENT_REG_SET(env, par, par64);
|
|
}
|
|
|
|
static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
uint64_t value)
|
|
{
|
|
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
|
uint64_t par64;
|
|
|
|
/* There is no SecureEL2 for AArch32. */
|
|
par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2,
|
|
ARMSS_NonSecure);
|
|
|
|
A32_BANKED_CURRENT_REG_SET(env, par, par64);
|
|
}
|
|
|
|
static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
bool isread)
|
|
{
|
|
/*
|
|
* R_NYXTL: instruction is UNDEFINED if it applies to an Exception level
|
|
* lower than EL3 and the combination SCR_EL3.{NSE,NS} is reserved. This can
|
|
* only happen when executing at EL3 because that combination also causes an
|
|
* illegal exception return. We don't need to check FEAT_RME either, because
|
|
* scr_write() ensures that the NSE bit is not set otherwise.
|
|
*/
|
|
if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) {
|
|
return CP_ACCESS_UNDEFINED;
|
|
}
|
|
return CP_ACCESS_OK;
|
|
}
|
|
|
|
static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
bool isread)
|
|
{
|
|
if (arm_current_el(env) == 3 &&
|
|
!(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) {
|
|
return CP_ACCESS_UNDEFINED;
|
|
}
|
|
return at_e012_access(env, ri, isread);
|
|
}
|
|
|
|
static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
bool isread)
|
|
{
|
|
if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) {
|
|
return CP_ACCESS_TRAP_EL2;
|
|
}
|
|
return at_e012_access(env, ri, isread);
|
|
}
|
|
|
|
static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
uint64_t value)
|
|
{
|
|
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
|
ARMMMUIdx mmu_idx;
|
|
uint64_t hcr_el2 = arm_hcr_el2_eff(env);
|
|
bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE);
|
|
bool for_el3 = false;
|
|
ARMSecuritySpace ss;
|
|
|
|
switch (ri->opc2 & 6) {
|
|
case 0:
|
|
switch (ri->opc1) {
|
|
case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */
|
|
if (ri->crm == 9 && arm_pan_enabled(env)) {
|
|
mmu_idx = regime_e20 ?
|
|
ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN;
|
|
} else {
|
|
mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1;
|
|
}
|
|
break;
|
|
case 4: /* AT S1E2R, AT S1E2W */
|
|
mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2;
|
|
break;
|
|
case 6: /* AT S1E3R, AT S1E3W */
|
|
mmu_idx = ARMMMUIdx_E3;
|
|
for_el3 = true;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
break;
|
|
case 2: /* AT S1E0R, AT S1E0W */
|
|
mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_Stage1_E0;
|
|
break;
|
|
case 4: /* AT S12E1R, AT S12E1W */
|
|
mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E10_1;
|
|
break;
|
|
case 6: /* AT S12E0R, AT S12E0W */
|
|
mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_E10_0;
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env);
|
|
env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss);
|
|
}
|
|
|
|
static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
|
bool isread)
|
|
{
|
|
if (ri->opc2 & 4) {
|
|
/*
|
|
* The ATS12NSO* operations must trap to EL3 or EL2 if executed in
|
|
* Secure EL1 (which can only happen if EL3 is AArch64).
|
|
* They are simply UNDEF if executed from NS EL1.
|
|
* They function normally from EL2 or EL3.
|
|
*/
|
|
if (arm_current_el(env) == 1) {
|
|
if (arm_is_secure_below_el3(env)) {
|
|
if (env->cp15.scr_el3 & SCR_EEL2) {
|
|
return CP_ACCESS_TRAP_EL2;
|
|
}
|
|
return CP_ACCESS_TRAP_EL3;
|
|
}
|
|
return CP_ACCESS_UNDEFINED;
|
|
}
|
|
}
|
|
return CP_ACCESS_OK;
|
|
}
|
|
|
|
static const ARMCPRegInfo vapa_ats_reginfo[] = {
|
|
/* This underdecoding is safe because the reginfo is NO_RAW. */
|
|
{ .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY,
|
|
.access = PL1_W, .accessfn = ats_access,
|
|
.writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
|
|
};
|
|
|
|
static const ARMCPRegInfo v8_ats_reginfo[] = {
|
|
/* 64 bit address translation operations */
|
|
{ .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E1R,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E1W,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E0R,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E0W,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4,
|
|
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.accessfn = at_e012_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5,
|
|
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.accessfn = at_e012_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6,
|
|
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.accessfn = at_e012_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7,
|
|
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.accessfn = at_e012_access, .writefn = ats_write64 },
|
|
/* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */
|
|
{ .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0,
|
|
.access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.writefn = ats_write64 },
|
|
{ .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1,
|
|
.access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.writefn = ats_write64 },
|
|
};
|
|
|
|
static const ARMCPRegInfo el2_ats_reginfo[] = {
|
|
/*
|
|
* Unlike the other EL2-related AT operations, these must
|
|
* UNDEF from EL3 if EL2 is not implemented, which is why we
|
|
* define them here rather than with the rest of the AT ops.
|
|
*/
|
|
{ .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0,
|
|
.access = PL2_W, .accessfn = at_s1e2_access,
|
|
.type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF,
|
|
.writefn = ats_write64 },
|
|
{ .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1,
|
|
.access = PL2_W, .accessfn = at_s1e2_access,
|
|
.type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF,
|
|
.writefn = ats_write64 },
|
|
/*
|
|
* The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE
|
|
* if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3
|
|
* with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose
|
|
* to behave as if SCR.NS was 1.
|
|
*/
|
|
{ .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0,
|
|
.access = PL2_W,
|
|
.writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
|
|
{ .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1,
|
|
.access = PL2_W,
|
|
.writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
|
|
};
|
|
|
|
static const ARMCPRegInfo ats1e1_reginfo[] = {
|
|
{ .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E1RP,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
{ .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64,
|
|
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.fgt = FGT_ATS1E1WP,
|
|
.accessfn = at_s1e01_access, .writefn = ats_write64 },
|
|
};
|
|
|
|
static const ARMCPRegInfo ats1cp_reginfo[] = {
|
|
{ .name = "ATS1CPRP",
|
|
.cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.writefn = ats_write },
|
|
{ .name = "ATS1CPWP",
|
|
.cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1,
|
|
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
|
.writefn = ats_write },
|
|
};
|
|
|
|
void define_at_insn_regs(ARMCPU *cpu)
|
|
{
|
|
CPUARMState *env = &cpu->env;
|
|
|
|
if (arm_feature(env, ARM_FEATURE_VAPA)) {
|
|
define_arm_cp_regs(cpu, vapa_ats_reginfo);
|
|
}
|
|
if (arm_feature(env, ARM_FEATURE_V8)) {
|
|
define_arm_cp_regs(cpu, v8_ats_reginfo);
|
|
}
|
|
if (arm_feature(env, ARM_FEATURE_EL2)
|
|
|| (arm_feature(env, ARM_FEATURE_EL3)
|
|
&& arm_feature(env, ARM_FEATURE_V8))) {
|
|
define_arm_cp_regs(cpu, el2_ats_reginfo);
|
|
}
|
|
if (cpu_isar_feature(aa64_ats1e1, cpu)) {
|
|
define_arm_cp_regs(cpu, ats1e1_reginfo);
|
|
}
|
|
if (cpu_isar_feature(aa32_ats1e1, cpu)) {
|
|
define_arm_cp_regs(cpu, ats1cp_reginfo);
|
|
}
|
|
}
|