diff options
Diffstat (limited to 'target-arm/helper.c')
-rw-r--r-- | target-arm/helper.c | 145 |
1 files changed, 101 insertions, 44 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index f630d96306..2a77c97c7b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -389,12 +389,6 @@ static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, } static const ARMCPRegInfo cp_reginfo[] = { - /* DBGDIDR: just RAZ. In particular this means the "debug architecture - * version" bits will read as a reserved value, which should cause - * Linux to not try to use the debug hardware. - */ - { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "FCSEIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, @@ -471,6 +465,13 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY, .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, .resetvalue = 0 }, + /* We don't implement pre-v7 debug but most CPUs had at least a DBGDIDR; + * implementing it as RAZ means the "debug architecture version" bits + * will read as a reserved value, which should cause Linux to not try + * to use the debug hardware. + */ + { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -712,13 +713,6 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) } static const ARMCPRegInfo v7_cp_reginfo[] = { - /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped - * debug components - */ - { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .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 }, /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */ { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_NOP }, @@ -1734,11 +1728,6 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, - /* 64 bit access versions of the (dummy) debug registers */ - { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, - { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0, .access = PL1_RW, .type = ARM_CP_64BIT, .fieldoffset = offsetof(CPUARMState, cp15.par_el1), .resetvalue = 0 }, @@ -2083,16 +2072,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc1 = 0, .crn = 3, .crm = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c3), .resetvalue = 0, .writefn = dacr_write, .raw_writefn = raw_write, }, - /* Dummy implementation of monitor debug system control register: - * we don't support debug. - */ - { .name = "MDSCR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* We define a dummy WI OSLAR_EL1, because Linux writes to it. */ - { .name = "OSLAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, - .access = PL1_W, .type = ARM_CP_NOP }, { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_NO_MIGRATE, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, @@ -2206,29 +2185,98 @@ static CPAccessResult ctr_el0_access(CPUARMState *env, const ARMCPRegInfo *ri) return CP_ACCESS_OK; } -static void define_aarch64_debug_regs(ARMCPU *cpu) +static const ARMCPRegInfo debug_cp_reginfo[] = { + /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped + * debug components. The AArch64 version of DBGDRAR is named MDRAR_EL1; + * unlike DBGDRAR it is never accessible from EL0. + * DBGDSAR is deprecated and must RAZ from v8 anyway, so it has no AArch64 + * accessor. + */ + { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .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 }, + { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* Dummy implementation of monitor debug system control register: + * we don't support debug. (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, + .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), + .resetvalue = 0 }, + /* We define a dummy WI OSLAR_EL1, because Linux writes to it. */ + { .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_NOP }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { + /* 64 bit access versions of the (dummy) debug registers */ + { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, + .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +static void define_debug_regs(ARMCPU *cpu) { - /* Define breakpoint and watchpoint registers. These do nothing - * but read as written, for now. + /* Define v7 and v8 architectural debug registers. + * These are just dummy implementations for now. */ int i; + int wrps, brps; + ARMCPRegInfo dbgdidr = { + .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, + }; - for (i = 0; i < 16; i++) { + brps = extract32(cpu->dbgdidr, 24, 4); + wrps = extract32(cpu->dbgdidr, 28, 4); + + /* The DBGDIDR and ID_AA64DFR0_EL1 define various properties + * of the debug registers such as number of breakpoints; + * check that if they both exist then they agree. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + assert(extract32(cpu->id_aa64dfr0, 12, 4) == brps); + assert(extract32(cpu->id_aa64dfr0, 20, 4) == wrps); + } + + define_one_arm_cp_reg(cpu, &dbgdidr); + define_arm_cp_regs(cpu, debug_cp_reginfo); + + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + define_arm_cp_regs(cpu, debug_lpae_cp_reginfo); + } + + for (i = 0; i < brps + 1; i++) { ARMCPRegInfo dbgregs[] = { - { .name = "DBGBVR", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, + { .name = "DBGBVR", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]) }, - { .name = "DBGBCR", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, + { .name = "DBGBCR", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]) }, - { .name = "DBGWVR", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, dbgregs); + } + + for (i = 0; i < wrps + 1; i++) { + ARMCPRegInfo dbgregs[] = { + { .name = "DBGWVR", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]) }, - { .name = "DBGWCR", .state = ARM_CP_STATE_AA64, - .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, + { .name = "DBGWCR", .state = ARM_CP_STATE_BOTH, + .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]) }, REGINFO_SENTINEL @@ -2353,6 +2401,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); + define_debug_regs(cpu); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } @@ -2426,7 +2475,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &rvbar); define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); - define_aarch64_debug_regs(cpu); } if (arm_feature(env, ARM_FEATURE_EL2)) { define_arm_cp_regs(cpu, v8_el2_cp_reginfo); @@ -2779,9 +2827,11 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, /* The AArch32 view of a shared register sees the lower 32 bits * of a 64 bit backing field. It is not migratable as the AArch64 * view handles that. AArch64 also handles reset. - * We assume it is a cp15 register. + * We assume it is a cp15 register if the .cp field is left unset. */ - r2->cp = 15; + if (r2->cp == 0) { + r2->cp = 15; + } r2->type |= ARM_CP_NO_MIGRATE; r2->resetfn = arm_cp_reset_ignore; #ifdef HOST_WORDS_BIGENDIAN @@ -2794,8 +2844,11 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, /* To allow abbreviation of ARMCPRegInfo * definitions, we treat cp == 0 as equivalent to * the value for "standard guest-visible sysreg". + * STATE_BOTH definitions are also always "standard + * sysreg" in their AArch64 view (the .cp value may + * be non-zero for the benefit of the AArch32 view). */ - if (r->cp == 0) { + if (r->cp == 0 || r->state == ARM_CP_STATE_BOTH) { r2->cp = CP_REG_ARM64_SYSREG_CP; } *key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm, @@ -3499,6 +3552,10 @@ void arm_cpu_do_interrupt(CPUState *cs) addr += env->cp15.vbar_el[1]; } switch_mode (env, new_mode); + /* For exceptions taken to AArch32 we must clear the SS bit in both + * PSTATE and in the old-state value we save to SPSR_<mode>, so zero it now. + */ + env->uncached_cpsr &= ~PSTATE_SS; env->spsr = cpsr_read(env); /* Clear IT bits. */ env->condexec_bits = 0; |