static CPUState *do_raise_exception(CPUARMState *env, uint32_t excp,
uint32_t syndrome, uint32_t target_el)
{
- CPUState *cs = CPU(arm_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) {
/*
return val;
}
-#if !defined(CONFIG_USER_ONLY)
-
-static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
- unsigned int target_el,
- bool same_el, bool ea,
- bool s1ptw, bool is_write,
- int fsc)
-{
- uint32_t syn;
-
- /* ISV is only set for data aborts routed to EL2 and
- * never for stage-1 page table walks faulting on stage 2.
- *
- * Furthermore, ISV is only set for certain kinds of load/stores.
- * If the template syndrome does not have ISV set, we should leave
- * it cleared.
- *
- * See ARMv8 specs, D7-1974:
- * ISS encoding for an exception from a Data Abort, the
- * ISV field.
- */
- if (!(template_syn & ARM_EL_ISV) || target_el != 2 || s1ptw) {
- syn = syn_data_abort_no_iss(same_el,
- ea, 0, s1ptw, is_write, fsc);
- } else {
- /* Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template
- * syndrome created at translation time.
- * Now we create the runtime syndrome with the remaining fields.
- */
- syn = syn_data_abort_with_iss(same_el,
- 0, 0, 0, 0, 0,
- ea, 0, s1ptw, is_write, fsc,
- false);
- /* Merge the runtime syndrome with the template syndrome. */
- syn |= template_syn;
- }
- return syn;
-}
-
-static void deliver_fault(ARMCPU *cpu, vaddr addr, MMUAccessType access_type,
- int mmu_idx, ARMMMUFaultInfo *fi)
-{
- CPUARMState *env = &cpu->env;
- int target_el;
- bool same_el;
- uint32_t syn, exc, fsr, fsc;
- ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx);
-
- target_el = exception_target_el(env);
- if (fi->stage2) {
- target_el = 2;
- env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4;
- }
- same_el = (arm_current_el(env) == target_el);
-
- if (target_el == 2 || arm_el_is_aa64(env, target_el) ||
- arm_s1_regime_using_lpae_format(env, arm_mmu_idx)) {
- /* LPAE format fault status register : bottom 6 bits are
- * status code in the same form as needed for syndrome
- */
- fsr = arm_fi_to_lfsc(fi);
- fsc = extract32(fsr, 0, 6);
- } else {
- fsr = arm_fi_to_sfsc(fi);
- /* Short format FSR : this fault will never actually be reported
- * to an EL that uses a syndrome register. Use a (currently)
- * reserved FSR code in case the constructed syndrome does leak
- * into the guest somehow.
- */
- fsc = 0x3f;
- }
-
- if (access_type == MMU_INST_FETCH) {
- syn = syn_insn_abort(same_el, fi->ea, fi->s1ptw, fsc);
- exc = EXCP_PREFETCH_ABORT;
- } else {
- syn = merge_syn_data_abort(env->exception.syndrome, target_el,
- same_el, fi->ea, fi->s1ptw,
- access_type == MMU_DATA_STORE,
- fsc);
- if (access_type == MMU_DATA_STORE
- && arm_feature(env, ARM_FEATURE_V6)) {
- fsr |= (1 << 11);
- }
- exc = EXCP_DATA_ABORT;
- }
-
- env->exception.vaddress = addr;
- env->exception.fsr = fsr;
- raise_exception(env, exc, syn, target_el);
-}
-
-/* try to fill the TLB and return an exception if error. If retaddr is
- * NULL, it means that the function was called in C code (i.e. not
- * from generated code or from helper.c)
- */
-void tlb_fill(CPUState *cs, target_ulong addr, int size,
- MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
-{
- bool ret;
- ARMMMUFaultInfo fi = {};
-
- ret = arm_tlb_fill(cs, addr, access_type, mmu_idx, &fi);
- if (unlikely(ret)) {
- ARMCPU *cpu = ARM_CPU(cs);
-
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr, true);
-
- deliver_fault(cpu, addr, access_type, mmu_idx, &fi);
- }
-}
-
-/* Raise a data fault alignment exception for the specified virtual address */
-void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
- MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- ARMMMUFaultInfo fi = {};
-
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr, true);
-
- fi.type = ARMFault_Alignment;
- deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi);
-}
-
-/* arm_cpu_do_transaction_failed: handle a memory system error response
- * (eg "no device/memory present at address") by raising an external abort
- * exception
- */
-void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
- vaddr addr, unsigned size,
- MMUAccessType access_type,
- int mmu_idx, MemTxAttrs attrs,
- MemTxResult response, uintptr_t retaddr)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- ARMMMUFaultInfo fi = {};
-
- /* now we have a real cpu fault */
- cpu_restore_state(cs, retaddr, true);
-
- fi.ea = arm_extabort_type(response);
- fi.type = ARMFault_SyncExternal;
- deliver_fault(cpu, addr, access_type, mmu_idx, &fi);
-}
-
-#endif /* !defined(CONFIG_USER_ONLY) */
-
void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue)
{
/*
* raising an exception if the limit is breached.
*/
if (newvalue < v7m_sp_limit(env)) {
- CPUState *cs = CPU(arm_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
/*
* Stack limit exceptions are a rare case, so rather than syncing
return res;
}
-uint32_t HELPER(double_saturate)(CPUARMState *env, int32_t val)
-{
- uint32_t res;
- if (val >= 0x40000000) {
- res = ~SIGNBIT;
- env->QF = 1;
- } else if (val <= (int32_t)0xc0000000) {
- res = SIGNBIT;
- env->QF = 1;
- } else {
- res = val << 1;
- }
- return res;
-}
-
uint32_t HELPER(add_usaturate)(CPUARMState *env, uint32_t a, uint32_t b)
{
uint32_t res = a + b;
void HELPER(setend)(CPUARMState *env)
{
env->uncached_cpsr ^= CPSR_E;
+ arm_rebuild_hflags(env);
}
/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped.
void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
{
- CPUState *cs = CPU(arm_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
int target_el = check_wfx_trap(env, false);
if (cpu_has_work(cs)) {
}
if (target_el) {
- env->pc -= insn_len;
+ if (env->aarch64) {
+ env->pc -= insn_len;
+ } else {
+ env->regs[15] -= insn_len;
+ }
+
raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2),
target_el);
}
void HELPER(yield)(CPUARMState *env)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
- CPUState *cs = CPU(cpu);
+ CPUState *cs = env_cpu(env);
/* This is a non-trappable hint instruction that generally indicates
* that the guest is currently busy-looping. Yield control back to the
*/
void HELPER(exception_internal)(CPUARMState *env, uint32_t excp)
{
- CPUState *cs = CPU(arm_env_get_cpu(env));
+ CPUState *cs = env_cpu(env);
assert(excp_is_internal(excp));
cs->exception_index = excp;
*/
void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome)
{
+ int debug_el = arm_debug_target_el(env);
+ int cur_el = arm_current_el(env);
+
/* FSR will only be used if the debug target EL is AArch32. */
env->exception.fsr = arm_debug_exception_fsr(env);
/* FAR is UNKNOWN: clear vaddress to avoid potentially exposing
* exception/security level.
*/
env->exception.vaddress = 0;
- raise_exception(env, EXCP_BKPT, syndrome, arm_debug_target_el(env));
+ /*
+ * Other kinds of architectural debug exception are ignored if
+ * they target an exception level below the current one (in QEMU
+ * this is checked by arm_generate_debug_exceptions()). Breakpoint
+ * instructions are special because they always generate an exception
+ * to somewhere: if they can't go to the configured debug exception
+ * level they are taken to the current exception level.
+ */
+ if (debug_el < cur_el) {
+ debug_el = cur_el;
+ }
+ raise_exception(env, EXCP_BKPT, syndrome, debug_el);
}
uint32_t HELPER(cpsr_read)(CPUARMState *env)
{
- return cpsr_read(env) & ~(CPSR_EXEC | CPSR_RESERVED);
+ /*
+ * We store the ARMv8 PSTATE.SS bit in env->uncached_cpsr.
+ * This is convenient for populating SPSR_ELx, but must be
+ * hidden from aarch32 mode, where it is not visible.
+ *
+ * TODO: ARMv8.4-DIT -- need to move SS somewhere else.
+ */
+ return cpsr_read(env) & ~(CPSR_EXEC | PSTATE_SS);
}
void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask)
{
cpsr_write(env, val, mask, CPSRWriteByInstr);
+ /* TODO: Not all cpsr bits are relevant to hflags. */
+ arm_rebuild_hflags(env);
}
/* Write the CPSR for a 32-bit exception return */
void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
{
+ uint32_t mask;
+
qemu_mutex_lock_iothread();
- arm_call_pre_el_change_hook(arm_env_get_cpu(env));
+ arm_call_pre_el_change_hook(env_archcpu(env));
qemu_mutex_unlock_iothread();
- cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn);
+ mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar);
+ cpsr_write(env, val, mask, CPSRWriteExceptionReturn);
/* Generated code has already stored the new PC value, but
* without masking out its low bits, because which bits need
* state. Do the masking now.
*/
env->regs[15] &= (env->thumb ? ~1 : ~3);
+ arm_rebuild_hflags(env);
qemu_mutex_lock_iothread();
- arm_call_el_change_hook(arm_env_get_cpu(env));
+ arm_call_el_change_hook(env_archcpu(env));
qemu_mutex_unlock_iothread();
}
raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env));
}
+ /*
+ * Check for an EL2 trap due to HSTR_EL2. We expect EL0 accesses
+ * to sysregs non accessible at EL0 to have UNDEF-ed already.
+ */
+ if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 &&
+ (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) {
+ uint32_t mask = 1 << ri->crn;
+
+ if (ri->type & ARM_CP_64BIT) {
+ mask = 1 << ri->crm;
+ }
+
+ /* T4 and T14 are RES0 */
+ mask &= ~((1 << 4) | (1 << 14));
+
+ if (env->cp15.hstr_el2 & mask) {
+ target_el = 2;
+ goto exept;
+ }
+ }
+
if (!ri->accessfn) {
return;
}
g_assert_not_reached();
}
+exept:
raise_exception(env, EXCP_UDEF, syndrome, target_el);
}
return res;
}
-void HELPER(clear_pstate_ss)(CPUARMState *env)
-{
- env->pstate &= ~PSTATE_SS;
-}
-
void HELPER(pre_hvc)(CPUARMState *env)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
+ ARMCPU *cpu = env_archcpu(env);
int cur_el = arm_current_el(env);
/* FIXME: Use actual secure state. */
bool secure = false;
void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
{
- ARMCPU *cpu = arm_env_get_cpu(env);
+ ARMCPU *cpu = env_archcpu(env);
int cur_el = arm_current_el(env);
bool secure = arm_is_secure(env);
bool smd_flag = env->cp15.scr_el3 & SCR_SMD;
}
}
-/* Return true if the linked breakpoint entry lbn passes its checks */
-static bool linked_bp_matches(ARMCPU *cpu, int lbn)
-{
- CPUARMState *env = &cpu->env;
- uint64_t bcr = env->cp15.dbgbcr[lbn];
- int brps = extract32(cpu->dbgdidr, 24, 4);
- int ctx_cmps = extract32(cpu->dbgdidr, 20, 4);
- int bt;
- uint32_t contextidr;
-
- /* Links to unimplemented or non-context aware breakpoints are
- * CONSTRAINED UNPREDICTABLE: either behave as if disabled, or
- * as if linked to an UNKNOWN context-aware breakpoint (in which
- * case DBGWCR<n>_EL1.LBN must indicate that breakpoint).
- * We choose the former.
- */
- if (lbn > brps || lbn < (brps - ctx_cmps)) {
- return false;
- }
-
- bcr = env->cp15.dbgbcr[lbn];
-
- if (extract64(bcr, 0, 1) == 0) {
- /* Linked breakpoint disabled : generate no events */
- return false;
- }
-
- bt = extract64(bcr, 20, 4);
-
- /* We match the whole register even if this is AArch32 using the
- * short descriptor format (in which case it holds both PROCID and ASID),
- * since we don't implement the optional v7 context ID masking.
- */
- contextidr = extract64(env->cp15.contextidr_el[1], 0, 32);
-
- switch (bt) {
- case 3: /* linked context ID match */
- if (arm_current_el(env) > 1) {
- /* Context matches never fire in EL2 or (AArch64) EL3 */
- return false;
- }
- return (contextidr == extract64(env->cp15.dbgbvr[lbn], 0, 32));
- case 5: /* linked address mismatch (reserved in AArch64) */
- case 9: /* linked VMID match (reserved if no EL2) */
- case 11: /* linked context ID and VMID match (reserved if no EL2) */
- default:
- /* Links to Unlinked context breakpoints must generate no
- * events; we choose to do the same for reserved values too.
- */
- return false;
- }
-
- return false;
-}
-
-static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp)
-{
- CPUARMState *env = &cpu->env;
- uint64_t cr;
- int pac, hmc, ssc, wt, lbn;
- /* Note that for watchpoints the check is against the CPU security
- * state, not the S/NS attribute on the offending data access.
- */
- bool is_secure = arm_is_secure(env);
- int access_el = arm_current_el(env);
-
- if (is_wp) {
- CPUWatchpoint *wp = env->cpu_watchpoint[n];
-
- if (!wp || !(wp->flags & BP_WATCHPOINT_HIT)) {
- return false;
- }
- cr = env->cp15.dbgwcr[n];
- if (wp->hitattrs.user) {
- /* The LDRT/STRT/LDT/STT "unprivileged access" instructions should
- * match watchpoints as if they were accesses done at EL0, even if
- * the CPU is at EL1 or higher.
- */
- access_el = 0;
- }
- } 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; 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(cr, 1, 2);
- hmc = extract64(cr, 13, 1);
- ssc = extract64(cr, 14, 2);
-
- switch (ssc) {
- case 0:
- break;
- case 1:
- case 3:
- if (is_secure) {
- return false;
- }
- break;
- case 2:
- if (!is_secure) {
- return false;
- }
- break;
- }
-
- switch (access_el) {
- case 3:
- case 2:
- if (!hmc) {
- return false;
- }
- break;
- case 1:
- if (extract32(pac, 0, 1) == 0) {
- return false;
- }
- break;
- case 0:
- if (extract32(pac, 1, 1) == 0) {
- return false;
- }
- break;
- default:
- g_assert_not_reached();
- }
-
- wt = extract64(cr, 20, 1);
- lbn = extract64(cr, 16, 4);
-
- if (wt && !linked_bp_matches(cpu, lbn)) {
- return false;
- }
-
- return true;
-}
-
-static bool check_watchpoints(ARMCPU *cpu)
-{
- CPUARMState *env = &cpu->env;
- int n;
-
- /* If watchpoints are disabled globally or we can't take debug
- * exceptions here then watchpoint 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_watchpoint); 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;
- }
- }
- return false;
-}
-
-void HELPER(check_breakpoints)(CPUARMState *env)
-{
- ARMCPU *cpu = arm_env_get_cpu(env);
-
- if (check_breakpoints(cpu)) {
- HELPER(exception_internal(env, EXCP_DEBUG));
- }
-}
-
-bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
-{
- /* Called by core code when a CPU watchpoint fires; need to check if this
- * is also an architectural watchpoint match.
- */
- ARMCPU *cpu = ARM_CPU(cs);
-
- return check_watchpoints(cpu);
-}
-
-vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
-
- /* In BE32 system mode, target memory is stored byteswapped (on a
- * little-endian host system), and by the time we reach here (via an
- * opcode helper) the addresses of subword accesses have been adjusted
- * to account for that, which means that watchpoints will not match.
- * Undo the adjustment here.
- */
- if (arm_sctlr_b(env)) {
- if (len == 1) {
- addr ^= 3;
- } else if (len == 2) {
- addr ^= 2;
- }
- }
-
- return addr;
-}
-
-void arm_debug_excp_handler(CPUState *cs)
-{
- /* Called by core code when a watchpoint or breakpoint fires;
- * need to check which one and raise the appropriate exception.
- */
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
- CPUWatchpoint *wp_hit = cs->watchpoint_hit;
-
- if (wp_hit) {
- if (wp_hit->flags & BP_CPU) {
- bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0;
- bool same_el = arm_debug_target_el(env) == arm_current_el(env);
-
- cs->watchpoint_hit = NULL;
-
- env->exception.fsr = arm_debug_exception_fsr(env);
- env->exception.vaddress = wp_hit->hitaddr;
- raise_exception(env, EXCP_DATA_ABORT,
- syn_watchpoint(same_el, 0, wnr),
- arm_debug_target_el(env));
- }
- } else {
- uint64_t pc = is_a64(env) ? env->pc : env->regs[15];
- bool same_el = (arm_debug_target_el(env) == arm_current_el(env));
-
- /* (1) GDB breakpoints should be handled first.
- * (2) Do not raise a CPU exception if no CPU breakpoint has fired,
- * since singlestep is also done by generating a debug internal
- * exception.
- */
- if (cpu_breakpoint_test(cs, pc, BP_GDB)
- || !cpu_breakpoint_test(cs, pc, BP_CPU)) {
- return;
- }
-
- env->exception.fsr = arm_debug_exception_fsr(env);
- /* FAR is UNKNOWN: clear vaddress to avoid potentially exposing
- * values to the guest that it shouldn't be able to see at its
- * exception/security level.
- */
- env->exception.vaddress = 0;
- raise_exception(env, EXCP_PREFETCH_ABORT,
- syn_breakpoint(same_el),
- arm_debug_target_el(env));
- }
-}
-
/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
The only way to do that in TCG is a conditional branch, which clobbers
all our temporaries. For now implement these as helper functions. */