aboutsummaryrefslogtreecommitdiff
path: root/target-arm
diff options
context:
space:
mode:
Diffstat (limited to 'target-arm')
-rw-r--r--target-arm/cpu-qom.h2
-rw-r--r--target-arm/cpu.c2
-rw-r--r--target-arm/cpu.h29
-rw-r--r--target-arm/cpu64.c2
-rw-r--r--target-arm/helper.c214
-rw-r--r--target-arm/internals.h26
-rw-r--r--target-arm/op_helper.c51
-rw-r--r--target-arm/translate.c67
8 files changed, 306 insertions, 87 deletions
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index 07c0a71942..1cc4502fc4 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -148,6 +148,8 @@ typedef struct ARMCPU {
uint32_t id_pfr0;
uint32_t id_pfr1;
uint32_t id_dfr0;
+ uint32_t pmceid0;
+ uint32_t pmceid1;
uint32_t id_afr0;
uint32_t id_mmfr0;
uint32_t id_mmfr1;
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index f2393cd9f2..e95b0307a6 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -1156,6 +1156,8 @@ static void cortex_a15_initfn(Object *obj)
cpu->id_pfr0 = 0x00001131;
cpu->id_pfr1 = 0x00011011;
cpu->id_dfr0 = 0x02010555;
+ cpu->pmceid0 = 0x0000000;
+ cpu->pmceid1 = 0x00000000;
cpu->id_afr0 = 0x00000000;
cpu->id_mmfr0 = 0x10201105;
cpu->id_mmfr1 = 0x20000000;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 5137632ccc..16238216f4 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -595,6 +595,18 @@ void pmccntr_sync(CPUARMState *env);
#define CPTR_TTA (1U << 20)
#define CPTR_TFP (1U << 10)
+#define MDCR_EPMAD (1U << 21)
+#define MDCR_EDAD (1U << 20)
+#define MDCR_SPME (1U << 17)
+#define MDCR_SDD (1U << 16)
+#define MDCR_TDRA (1U << 11)
+#define MDCR_TDOSA (1U << 10)
+#define MDCR_TDA (1U << 9)
+#define MDCR_TDE (1U << 8)
+#define MDCR_HPME (1U << 7)
+#define MDCR_TPM (1U << 6)
+#define MDCR_TPMCR (1U << 5)
+
#define CPSR_M (0x1fU)
#define CPSR_T (1U << 5)
#define CPSR_F (1U << 6)
@@ -1255,6 +1267,18 @@ static inline bool cptype_valid(int cptype)
#define PL1_RW (PL1_R | PL1_W)
#define PL0_RW (PL0_R | PL0_W)
+/* Return the highest implemented Exception Level */
+static inline int arm_highest_el(CPUARMState *env)
+{
+ if (arm_feature(env, ARM_FEATURE_EL3)) {
+ return 3;
+ }
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ return 2;
+ }
+ return 1;
+}
+
/* Return the current Exception Level (as per ARMv8; note that this differs
* from the ARMv7 Privilege Level).
*/
@@ -1310,6 +1334,11 @@ typedef enum CPAccessResult {
/* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */
CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5,
CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6,
+ /* Access fails and results in an exception syndrome for an FP access,
+ * trapped directly to EL2 or EL3
+ */
+ CP_ACCESS_TRAP_FP_EL2 = 7,
+ CP_ACCESS_TRAP_FP_EL3 = 8,
} CPAccessResult;
/* Access functions for coprocessor registers. These cannot fail and
diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
index c5bc19a405..fa5eda2cd1 100644
--- a/target-arm/cpu64.c
+++ b/target-arm/cpu64.c
@@ -135,6 +135,8 @@ static void aarch64_a57_initfn(Object *obj)
cpu->id_isar5 = 0x00011121;
cpu->id_aa64pfr0 = 0x00002222;
cpu->id_aa64dfr0 = 0x10305106;
+ cpu->pmceid0 = 0x00000000;
+ cpu->pmceid1 = 0x00000000;
cpu->id_aa64isar0 = 0x00011120;
cpu->id_aa64mmfr0 = 0x00001124;
cpu->dbgdidr = 0x3516d000;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 2f9db72806..5a0447b93a 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -385,6 +385,60 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env,
return CP_ACCESS_TRAP_UNCATEGORIZED;
}
+/* Check for traps to "powerdown debug" registers, which are controlled
+ * by MDCR.TDOSA
+ */
+static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDOSA)
+ && !arm_is_secure_below_el3(env)) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+}
+
+/* Check for traps to "debug ROM" registers, which are controlled
+ * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3.
+ */
+static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDRA)
+ && !arm_is_secure_below_el3(env)) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+}
+
+/* Check for traps to general debug registers, which are controlled
+ * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3.
+ */
+static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri,
+ bool isread)
+{
+ int el = arm_current_el(env);
+
+ if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDA)
+ && !arm_is_secure_below_el3(env)) {
+ return CP_ACCESS_TRAP_EL2;
+ }
+ if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) {
+ return CP_ACCESS_TRAP_EL3;
+ }
+ return CP_ACCESS_OK;
+}
+
static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
{
ARMCPU *cpu = arm_env_get_cpu(env);
@@ -1003,6 +1057,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.accessfn = pmreg_access,
.writefn = pmovsr_write,
.raw_writefn = raw_write },
+ { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
+ .access = PL0_RW, .accessfn = pmreg_access,
+ .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
+ .writefn = pmovsr_write,
+ .raw_writefn = raw_write },
/* Unimplemented so WI. */
{ .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
.access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
@@ -1044,6 +1105,12 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
.resetvalue = 0,
.writefn = pmuserenr_write, .raw_writefn = raw_write },
+ { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0,
+ .access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
+ .resetvalue = 0,
+ .writefn = pmuserenr_write, .raw_writefn = raw_write },
{ .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1,
.access = PL1_RW,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
@@ -1053,6 +1120,11 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.access = PL1_RW, .type = ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.writefn = pmintenclr_write, },
+ { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
+ .access = PL1_RW, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
+ .writefn = pmintenclr_write },
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .writefn = vbar_write,
@@ -1218,10 +1290,33 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = {
static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
- /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */
- if (arm_current_el(env) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2)) {
- return CP_ACCESS_TRAP;
+ /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero.
+ * Writable only at the highest implemented exception level.
+ */
+ int el = arm_current_el(env);
+
+ switch (el) {
+ case 0:
+ if (!extract32(env->cp15.c14_cntkctl, 0, 2)) {
+ return CP_ACCESS_TRAP;
+ }
+ break;
+ case 1:
+ if (!isread && ri->state == ARM_CP_STATE_AA32 &&
+ arm_is_secure_below_el3(env)) {
+ /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */
+ return CP_ACCESS_TRAP_UNCATEGORIZED;
+ }
+ break;
+ case 2:
+ case 3:
+ break;
+ }
+
+ if (!isread && el < arm_highest_el(env)) {
+ return CP_ACCESS_TRAP_UNCATEGORIZED;
}
+
return CP_ACCESS_OK;
}
@@ -2934,10 +3029,10 @@ static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) {
- return CP_ACCESS_TRAP_EL2;
+ return CP_ACCESS_TRAP_FP_EL2;
}
if (env->cp15.cptr_el[3] & CPTR_TFP) {
- return CP_ACCESS_TRAP_EL3;
+ return CP_ACCESS_TRAP_FP_EL3;
}
return CP_ACCESS_OK;
}
@@ -3325,7 +3420,8 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 1,
- .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ .access = PL2_RW, .accessfn = access_tda,
+ .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "HPFAR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4,
.access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any,
@@ -3732,16 +3828,19 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
* accessor.
*/
{ .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+ .access = PL0_R, .accessfn = access_tdra,
+ .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0,
- .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+ .access = PL1_R, .accessfn = access_tdra,
+ .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+ .access = PL0_R, .accessfn = access_tdra,
+ .type = ARM_CP_CONST, .resetvalue = 0 },
/* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */
{ .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2,
- .access = PL1_RW,
+ .access = PL1_RW, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1),
.resetvalue = 0 },
/* MDCCSR_EL0, aka DBGDSCRint. This is a read-only mirror of MDSCR_EL1.
@@ -3750,26 +3849,30 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
{ .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0,
.type = ARM_CP_ALIAS,
- .access = PL1_R,
+ .access = PL1_R, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), },
{ .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4,
.access = PL1_W, .type = ARM_CP_NO_RAW,
+ .accessfn = access_tdosa,
.writefn = oslar_write },
{ .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4,
.access = PL1_R, .resetvalue = 10,
+ .accessfn = access_tdosa,
.fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) },
/* Dummy OSDLR_EL1: 32-bit Linux will read this */
{ .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4,
- .access = PL1_RW, .type = ARM_CP_NOP },
+ .access = PL1_RW, .accessfn = access_tdosa,
+ .type = ARM_CP_NOP },
/* Dummy DBGVCR: Linux wants to clear this on startup, but we don't
* implement vector catch debug events yet.
*/
{ .name = "DBGVCR",
.cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0,
- .access = PL1_RW, .type = ARM_CP_NOP },
+ .access = PL1_RW, .accessfn = access_tda,
+ .type = ARM_CP_NOP },
REGINFO_SENTINEL
};
@@ -4034,7 +4137,8 @@ static void define_debug_regs(ARMCPU *cpu)
int wrps, brps, ctx_cmps;
ARMCPRegInfo dbgdidr = {
.name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr,
+ .access = PL0_R, .accessfn = access_tda,
+ .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr,
};
/* Note that all these register fields hold "number of Xs minus 1". */
@@ -4065,13 +4169,13 @@ static void define_debug_regs(ARMCPU *cpu)
ARMCPRegInfo dbgregs[] = {
{ .name = "DBGBVR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4,
- .access = PL1_RW,
+ .access = PL1_RW, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]),
.writefn = dbgbvr_write, .raw_writefn = raw_write
},
{ .name = "DBGBCR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5,
- .access = PL1_RW,
+ .access = PL1_RW, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]),
.writefn = dbgbcr_write, .raw_writefn = raw_write
},
@@ -4084,13 +4188,13 @@ static void define_debug_regs(ARMCPU *cpu)
ARMCPRegInfo dbgregs[] = {
{ .name = "DBGWVR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6,
- .access = PL1_RW,
+ .access = PL1_RW, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]),
.writefn = dbgwvr_write, .raw_writefn = raw_write
},
{ .name = "DBGWCR", .state = ARM_CP_STATE_BOTH,
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7,
- .access = PL1_RW,
+ .access = PL1_RW, .accessfn = access_tda,
.fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]),
.writefn = dbgwcr_write, .raw_writefn = raw_write
},
@@ -4294,6 +4398,22 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST,
.resetvalue = cpu->mvfr2 },
+ { .name = "PMCEID0", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid0 },
+ { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid0 },
+ { .name = "PMCEID1", .state = ARM_CP_STATE_AA32,
+ .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid1 },
+ { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7,
+ .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST,
+ .resetvalue = cpu->pmceid1 },
REGINFO_SENTINEL
};
/* RVBAR_EL1 is only implemented if EL1 is the highest EL */
@@ -5279,21 +5399,6 @@ void switch_mode(CPUARMState *env, int mode)
}
}
-void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val)
-{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- cpu_abort(CPU(cpu), "banked r13 write\n");
-}
-
-uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
-{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- cpu_abort(CPU(cpu), "banked r13 read\n");
- return 0;
-}
-
uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
uint32_t cur_el, bool secure)
{
@@ -5307,31 +5412,6 @@ void aarch64_sync_64_to_32(CPUARMState *env)
#else
-/* Map CPU modes onto saved register banks. */
-int bank_number(int mode)
-{
- switch (mode) {
- case ARM_CPU_MODE_USR:
- case ARM_CPU_MODE_SYS:
- return BANK_USRSYS;
- case ARM_CPU_MODE_SVC:
- return BANK_SVC;
- case ARM_CPU_MODE_ABT:
- return BANK_ABT;
- case ARM_CPU_MODE_UND:
- return BANK_UND;
- case ARM_CPU_MODE_IRQ:
- return BANK_IRQ;
- case ARM_CPU_MODE_FIQ:
- return BANK_FIQ;
- case ARM_CPU_MODE_HYP:
- return BANK_HYP;
- case ARM_CPU_MODE_MON:
- return BANK_MON;
- }
- g_assert_not_reached();
-}
-
void switch_mode(CPUARMState *env, int mode)
{
int old_mode;
@@ -7676,24 +7756,6 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
return phys_addr;
}
-void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val)
-{
- if ((env->uncached_cpsr & CPSR_M) == mode) {
- env->regs[13] = val;
- } else {
- env->banked_r13[bank_number(mode)] = val;
- }
-}
-
-uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
-{
- if ((env->uncached_cpsr & CPSR_M) == mode) {
- return env->regs[13];
- } else {
- return env->banked_r13[bank_number(mode)];
- }
-}
-
uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
{
ARMCPU *cpu = arm_env_get_cpu(env);
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 70bec4a4d2..2e70272be2 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -109,7 +109,31 @@ static inline unsigned int aarch64_banked_spsr_index(unsigned int el)
return map[el];
}
-int bank_number(int mode);
+/* Map CPU modes onto saved register banks. */
+static inline int bank_number(int mode)
+{
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_SYS:
+ return BANK_USRSYS;
+ case ARM_CPU_MODE_SVC:
+ return BANK_SVC;
+ case ARM_CPU_MODE_ABT:
+ return BANK_ABT;
+ case ARM_CPU_MODE_UND:
+ return BANK_UND;
+ case ARM_CPU_MODE_IRQ:
+ return BANK_IRQ;
+ case ARM_CPU_MODE_FIQ:
+ return BANK_FIQ;
+ case ARM_CPU_MODE_HYP:
+ return BANK_HYP;
+ case ARM_CPU_MODE_MON:
+ return BANK_MON;
+ }
+ g_assert_not_reached();
+}
+
void switch_mode(CPUARMState *, int);
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu);
void arm_translate_init(void);
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index bd48549826..538887ce0c 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -457,6 +457,32 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val)
}
}
+void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val)
+{
+ if ((env->uncached_cpsr & CPSR_M) == mode) {
+ env->regs[13] = val;
+ } else {
+ env->banked_r13[bank_number(mode)] = val;
+ }
+}
+
+uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode)
+{
+ if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SYS) {
+ /* SRS instruction is UNPREDICTABLE from System mode; we UNDEF.
+ * Other UNPREDICTABLE and UNDEF cases were caught at translate time.
+ */
+ raise_exception(env, EXCP_UDEF, syn_uncategorized(),
+ exception_target_el(env));
+ }
+
+ if ((env->uncached_cpsr & CPSR_M) == mode) {
+ return env->regs[13];
+ } else {
+ return env->banked_r13[bank_number(mode)];
+ }
+}
+
void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
uint32_t isread)
{
@@ -500,6 +526,19 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
target_el = 3;
syndrome = syn_uncategorized();
break;
+ case CP_ACCESS_TRAP_FP_EL2:
+ target_el = 2;
+ /* Since we are an implementation that takes exceptions on a trapped
+ * conditional insn only if the insn has passed its condition code
+ * check, we take the IMPDEF choice to always report CV=1 COND=0xe
+ * (which is also the required value for AArch64 traps).
+ */
+ syndrome = syn_fp_access_trap(1, 0xe, false);
+ break;
+ case CP_ACCESS_TRAP_FP_EL3:
+ target_el = 3;
+ syndrome = syn_fp_access_trap(1, 0xe, false);
+ break;
default:
g_assert_not_reached();
}
@@ -614,12 +653,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
int cur_el = arm_current_el(env);
bool secure = arm_is_secure(env);
bool smd = env->cp15.scr_el3 & SCR_SMD;
- /* On ARMv8 AArch32, SMD only applies to NS state.
- * On ARMv7 SMD only applies to NS state and only if EL2 is available.
- * For ARMv7 non EL2, we force SMD to zero so we don't need to re-check
- * the EL2 condition here.
+ /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state.
+ * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization
+ * extensions, SMD only applies to NS state.
+ * On ARMv7 without the Virtualization extensions, the SMD bit
+ * doesn't exist, but we forbid the guest to set it to 1 in scr_write(),
+ * so we need not special case this here.
*/
- bool undef = is_a64(env) ? smd : (!secure && smd);
+ bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure;
if (arm_is_psci_call(cpu, EXCP_SMC)) {
/* If PSCI is enabled and this looks like a valid PSCI call then
diff --git a/target-arm/translate.c b/target-arm/translate.c
index cf3dc33774..e69145d401 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -7578,8 +7578,67 @@ static void gen_srs(DisasContext *s,
uint32_t mode, uint32_t amode, bool writeback)
{
int32_t offset;
- TCGv_i32 addr = tcg_temp_new_i32();
- TCGv_i32 tmp = tcg_const_i32(mode);
+ TCGv_i32 addr, tmp;
+ bool undef = false;
+
+ /* SRS is:
+ * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1
+ * - UNDEFINED in Hyp mode
+ * - UNPREDICTABLE in User or System mode
+ * - UNPREDICTABLE if the specified mode is:
+ * -- not implemented
+ * -- not a valid mode number
+ * -- a mode that's at a higher exception level
+ * -- Monitor, if we are Non-secure
+ * For the UNPREDICTABLE cases we choose to UNDEF.
+ */
+ if (s->current_el == 1 && !s->ns) {
+ gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), 3);
+ return;
+ }
+
+ if (s->current_el == 0 || s->current_el == 2) {
+ undef = true;
+ }
+
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_FIQ:
+ case ARM_CPU_MODE_IRQ:
+ case ARM_CPU_MODE_SVC:
+ case ARM_CPU_MODE_ABT:
+ case ARM_CPU_MODE_UND:
+ case ARM_CPU_MODE_SYS:
+ break;
+ case ARM_CPU_MODE_HYP:
+ if (s->current_el == 1 || !arm_dc_feature(s, ARM_FEATURE_EL2)) {
+ undef = true;
+ }
+ break;
+ case ARM_CPU_MODE_MON:
+ /* No need to check specifically for "are we non-secure" because
+ * we've already made EL0 UNDEF and handled the trap for S-EL1;
+ * so if this isn't EL3 then we must be non-secure.
+ */
+ if (s->current_el != 3) {
+ undef = true;
+ }
+ break;
+ default:
+ undef = true;
+ }
+
+ if (undef) {
+ gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(),
+ default_exception_el(s));
+ return;
+ }
+
+ addr = tcg_temp_new_i32();
+ tmp = tcg_const_i32(mode);
+ /* get_r13_banked() will raise an exception if called from System mode */
+ gen_set_condexec(s);
+ gen_set_pc_im(s, s->pc - 4);
gen_helper_get_r13_banked(addr, cpu_env, tmp);
tcg_temp_free_i32(tmp);
switch (amode) {
@@ -7629,6 +7688,7 @@ static void gen_srs(DisasContext *s,
tcg_temp_free_i32(tmp);
}
tcg_temp_free_i32(addr);
+ s->is_jmp = DISAS_UPDATE;
}
static void disas_arm_insn(DisasContext *s, unsigned int insn)
@@ -7739,9 +7799,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
}
} else if ((insn & 0x0e5fffe0) == 0x084d0500) {
/* srs */
- if (IS_USER(s)) {
- goto illegal_op;
- }
ARCH(6);
gen_srs(s, (insn & 0x1f), (insn >> 23) & 3, insn & (1 << 21));
return;