diff options
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/cpu-qom.h | 2 | ||||
-rw-r--r-- | target-arm/cpu.c | 2 | ||||
-rw-r--r-- | target-arm/cpu.h | 29 | ||||
-rw-r--r-- | target-arm/cpu64.c | 2 | ||||
-rw-r--r-- | target-arm/helper.c | 214 | ||||
-rw-r--r-- | target-arm/internals.h | 26 | ||||
-rw-r--r-- | target-arm/op_helper.c | 51 | ||||
-rw-r--r-- | target-arm/translate.c | 67 |
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; |