mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-03-23 05:25:11 +00:00
target/arm: Take exceptions on ATS instructions when needed
The translation table walk for an ATS instruction can result in various faults. In general these are just reported back via the PAR_EL1 fault status fields, but in some cases the architecture requires that the fault is turned into an exception: * synchronous stage 2 faults of any kind during AT S1E0* and AT S1E1* instructions executed from NS EL1 fault to EL2 or EL3 * synchronous external aborts are taken as Data Abort exceptions (This is documented in the v8A Arm ARM DDI0487A.e D5.2.11 and G5.13.4.) Backports commit 0710b2fa84a4aeb925422e1e88edac49ed407c79 from qemu
This commit is contained in:
parent
56b54f361e
commit
9fb54a7f72
|
@ -2732,6 +2732,73 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
|
|||
ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &attrs,
|
||||
&prot, &page_size, &fi, &cacheattrs);
|
||||
|
||||
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_is_secure(env)
|
||||
&& (mmu_idx == ARMMMUIdx_S1NSE1 || mmu_idx == ARMMMUIdx_S1NSE0)) {
|
||||
/*
|
||||
* 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;
|
||||
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,
|
||||
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)) {
|
||||
|
@ -2936,7 +3003,7 @@ static const ARMCPRegInfo vapa_cp_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 },
|
||||
.writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
|
||||
#endif
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
@ -4092,35 +4159,45 @@ static const ARMCPRegInfo v8_cp_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, .writefn = ats_write64 },
|
||||
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.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, .writefn = ats_write64 },
|
||||
.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, .writefn = ats_write64 },
|
||||
.access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
|
||||
.writefn = ats_write64 },
|
||||
{ .name = "PAR_EL1", .state = ARM_CP_STATE_AA64,
|
||||
.type = ARM_CP_ALIAS,
|
||||
.opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0,
|
||||
|
@ -4703,11 +4780,11 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
|
|||
{ .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, .writefn = ats_write64 },
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .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, .writefn = ats_write64 },
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .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
|
||||
|
@ -4715,10 +4792,10 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
|
|||
*/
|
||||
{ .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0,
|
||||
.access = PL2_W,
|
||||
.writefn = ats1h_write, .type = ARM_CP_NO_RAW },
|
||||
.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 },
|
||||
.writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC },
|
||||
{ .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH,
|
||||
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0,
|
||||
/* ARMv7 requires bit 0 and 1 to reset to 1. ARMv8 defines the
|
||||
|
|
Loading…
Reference in a new issue