diff options
Diffstat (limited to 'target-arm/op_helper.c')
-rw-r--r-- | target-arm/op_helper.c | 143 |
1 files changed, 128 insertions, 15 deletions
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index b956216..03ac92a 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -301,6 +301,17 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome) { const ARMCPRegInfo *ri = rip; + + if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 + && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { + env->exception.syndrome = syndrome; + raise_exception(env, EXCP_UDEF); + } + + if (!ri->accessfn) { + return; + } + switch (ri->accessfn(env, ri)) { case CP_ACCESS_OK: return; @@ -374,6 +385,63 @@ void HELPER(clear_pstate_ss)(CPUARMState *env) env->pstate &= ~PSTATE_SS; } +void HELPER(pre_hvc)(CPUARMState *env) +{ + int cur_el = arm_current_pl(env); + /* FIXME: Use actual secure state. */ + bool secure = false; + bool undef; + + /* We've already checked that EL2 exists at translation time. + * EL3.HCE has priority over EL2.HCD. + */ + if (arm_feature(env, ARM_FEATURE_EL3)) { + undef = !(env->cp15.scr_el3 & SCR_HCE); + } else { + undef = env->cp15.hcr_el2 & HCR_HCD; + } + + /* In ARMv7 and ARMv8/AArch32, HVC is undef in secure state. + * For ARMv8/AArch64, HVC is allowed in EL3. + * Note that we've already trapped HVC from EL0 at translation + * time. + */ + if (secure && (!is_a64(env) || cur_el == 1)) { + undef = true; + } + + if (undef) { + env->exception.syndrome = syn_uncategorized(); + raise_exception(env, EXCP_UDEF); + } +} + +void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) +{ + int cur_el = arm_current_pl(env); + /* FIXME: Use real secure state. */ + bool secure = false; + 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. + */ + bool undef = is_a64(env) ? smd : (!secure && smd); + + /* In NS EL1, HCR controlled routing to EL2 has priority over SMD. */ + if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) { + env->exception.syndrome = syndrome; + raise_exception(env, EXCP_HYP_TRAP); + } + + /* We've already checked that EL3 exists at translation time. */ + if (undef) { + env->exception.syndrome = syn_uncategorized(); + raise_exception(env, EXCP_UDEF); + } +} + void HELPER(exception_return)(CPUARMState *env) { int cur_el = arm_current_pl(env); @@ -511,32 +579,43 @@ static bool linked_bp_matches(ARMCPU *cpu, int lbn) return false; } -static bool wp_matches(ARMCPU *cpu, int n) +static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) { CPUARMState *env = &cpu->env; - uint64_t wcr = env->cp15.dbgwcr[n]; + uint64_t cr; int pac, hmc, ssc, wt, lbn; /* TODO: check against CPU security state when we implement TrustZone */ bool is_secure = false; - if (!env->cpu_watchpoint[n] - || !(env->cpu_watchpoint[n]->flags & BP_WATCHPOINT_HIT)) { - return false; - } + if (is_wp) { + if (!env->cpu_watchpoint[n] + || !(env->cpu_watchpoint[n]->flags & BP_WATCHPOINT_HIT)) { + return false; + } + cr = env->cp15.dbgwcr[n]; + } else { + uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; + if (!env->cpu_breakpoint[n] || env->cpu_breakpoint[n]->pc != pc) { + return false; + } + cr = env->cp15.dbgbcr[n]; + } /* The WATCHPOINT_HIT flag guarantees us that the watchpoint is - * enabled and that the address and access type match; check the - * remaining fields, including linked breakpoints. - * Note that some combinations of {PAC, HMC SSC} are reserved and + * enabled and that the address and access type match; for breakpoints + * we know the address matched; check the remaining fields, including + * linked breakpoints. We rely on WCR and BCR having the same layout + * for the LBN, SSC, HMC, PAC/PMC and is-linked fields. + * Note that some combinations of {PAC, HMC, SSC} are reserved and * must act either like some valid combination or as if the watchpoint * were disabled. We choose the former, and use this together with * the fact that EL3 must always be Secure and EL2 must always be * Non-Secure to simplify the code slightly compared to the full * table in the ARM ARM. */ - pac = extract64(wcr, 1, 2); - hmc = extract64(wcr, 13, 1); - ssc = extract64(wcr, 14, 2); + pac = extract64(cr, 1, 2); + hmc = extract64(cr, 13, 1); + ssc = extract64(cr, 14, 2); switch (ssc) { case 0: @@ -560,6 +639,7 @@ static bool wp_matches(ARMCPU *cpu, int n) * Implementing this would require reworking the core watchpoint code * to plumb the mmu_idx through to this point. Luckily Linux does not * rely on this behaviour currently. + * For breakpoints we do want to use the current CPU state. */ switch (arm_current_pl(env)) { case 3: @@ -582,8 +662,8 @@ static bool wp_matches(ARMCPU *cpu, int n) g_assert_not_reached(); } - wt = extract64(wcr, 20, 1); - lbn = extract64(wcr, 16, 4); + wt = extract64(cr, 20, 1); + lbn = extract64(cr, 16, 4); if (wt && !linked_bp_matches(cpu, lbn)) { return false; @@ -606,7 +686,28 @@ static bool check_watchpoints(ARMCPU *cpu) } for (n = 0; n < ARRAY_SIZE(env->cpu_watchpoint); n++) { - if (wp_matches(cpu, n)) { + if (bp_wp_matches(cpu, n, true)) { + return true; + } + } + return false; +} + +static bool check_breakpoints(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + int n; + + /* If breakpoints are disabled globally or we can't take debug + * exceptions here then breakpoint firings are ignored. + */ + if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 + || !arm_generate_debug_exceptions(env)) { + return false; + } + + for (n = 0; n < ARRAY_SIZE(env->cpu_breakpoint); n++) { + if (bp_wp_matches(cpu, n, false)) { return true; } } @@ -641,6 +742,18 @@ void arm_debug_excp_handler(CPUState *cs) cpu_resume_from_signal(cs, NULL); } } + } else { + if (check_breakpoints(cpu)) { + bool same_el = (arm_debug_target_el(env) == arm_current_pl(env)); + env->exception.syndrome = syn_breakpoint(same_el); + if (extended_addresses_enabled(env)) { + env->exception.fsr = (1 << 9) | 0x22; + } else { + env->exception.fsr = 0x2; + } + /* FAR is UNKNOWN, so doesn't need setting */ + raise_exception(env, EXCP_PREFETCH_ABORT); + } } } |