aboutsummaryrefslogtreecommitdiff
path: root/target/arm/helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/arm/helper.c')
-rw-r--r--target/arm/helper.c92
1 files changed, 91 insertions, 1 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 8ef0fb478f..455c92b891 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -9581,6 +9581,66 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs)
take_aarch32_exception(env, new_mode, mask, offset, addr);
}
+static int aarch64_regnum(CPUARMState *env, int aarch32_reg)
+{
+ /*
+ * Return the register number of the AArch64 view of the AArch32
+ * register @aarch32_reg. The CPUARMState CPSR is assumed to still
+ * be that of the AArch32 mode the exception came from.
+ */
+ int mode = env->uncached_cpsr & CPSR_M;
+
+ switch (aarch32_reg) {
+ case 0 ... 7:
+ return aarch32_reg;
+ case 8 ... 12:
+ return mode == ARM_CPU_MODE_FIQ ? aarch32_reg + 16 : aarch32_reg;
+ case 13:
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_SYS:
+ return 13;
+ case ARM_CPU_MODE_HYP:
+ return 15;
+ case ARM_CPU_MODE_IRQ:
+ return 17;
+ case ARM_CPU_MODE_SVC:
+ return 19;
+ case ARM_CPU_MODE_ABT:
+ return 21;
+ case ARM_CPU_MODE_UND:
+ return 23;
+ case ARM_CPU_MODE_FIQ:
+ return 29;
+ default:
+ g_assert_not_reached();
+ }
+ case 14:
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_SYS:
+ case ARM_CPU_MODE_HYP:
+ return 14;
+ case ARM_CPU_MODE_IRQ:
+ return 16;
+ case ARM_CPU_MODE_SVC:
+ return 18;
+ case ARM_CPU_MODE_ABT:
+ return 20;
+ case ARM_CPU_MODE_UND:
+ return 22;
+ case ARM_CPU_MODE_FIQ:
+ return 30;
+ default:
+ g_assert_not_reached();
+ }
+ case 15:
+ return 31;
+ default:
+ g_assert_not_reached();
+ }
+}
+
/* Handle exception entry to a target EL which is using AArch64 */
static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
{
@@ -9591,6 +9651,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
unsigned int new_mode = aarch64_pstate_mode(new_el, true);
unsigned int old_mode;
unsigned int cur_el = arm_current_el(env);
+ int rt;
/*
* Note that new_el can never be 0. If cur_el is 0, then
@@ -9645,7 +9706,8 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
case EXCP_HVC:
case EXCP_HYP_TRAP:
case EXCP_SMC:
- if (syn_get_ec(env->exception.syndrome) == EC_ADVSIMDFPACCESSTRAP) {
+ switch (syn_get_ec(env->exception.syndrome)) {
+ case EC_ADVSIMDFPACCESSTRAP:
/*
* QEMU internal FP/SIMD syndromes from AArch32 include the
* TA and coproc fields which are only exposed if the exception
@@ -9653,6 +9715,34 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
* AArch64 format syndrome.
*/
env->exception.syndrome &= ~MAKE_64BIT_MASK(0, 20);
+ break;
+ case EC_CP14RTTRAP:
+ case EC_CP15RTTRAP:
+ case EC_CP14DTTRAP:
+ /*
+ * For a trap on AArch32 MRC/MCR/LDC/STC the Rt field is currently
+ * the raw register field from the insn; when taking this to
+ * AArch64 we must convert it to the AArch64 view of the register
+ * number. Notice that we read a 4-bit AArch32 register number and
+ * write back a 5-bit AArch64 one.
+ */
+ rt = extract32(env->exception.syndrome, 5, 4);
+ rt = aarch64_regnum(env, rt);
+ env->exception.syndrome = deposit32(env->exception.syndrome,
+ 5, 5, rt);
+ break;
+ case EC_CP15RRTTRAP:
+ case EC_CP14RRTTRAP:
+ /* Similarly for MRRC/MCRR traps for Rt and Rt2 fields */
+ rt = extract32(env->exception.syndrome, 5, 4);
+ rt = aarch64_regnum(env, rt);
+ env->exception.syndrome = deposit32(env->exception.syndrome,
+ 5, 5, rt);
+ rt = extract32(env->exception.syndrome, 10, 4);
+ rt = aarch64_regnum(env, rt);
+ env->exception.syndrome = deposit32(env->exception.syndrome,
+ 10, 5, rt);
+ break;
}
env->cp15.esr_el[new_el] = env->exception.syndrome;
break;