/* * CRIS helper routines. * * Copyright (c) 2007 AXIS Communications AB * Written by Edgar E. Iglesias. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include "qemu/osdep.h" #include "cpu.h" #include "mmu.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" //#define CRIS_HELPER_DEBUG #ifdef CRIS_HELPER_DEBUG #define D(x) x #define D_LOG(...) qemu_log(__VA_ARGS__) #else #define D(x) #define D_LOG(...) do { } while (0) #endif #if defined(CONFIG_USER_ONLY) void cris_cpu_do_interrupt(CPUState *cs) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; cs->exception_index = -1; env->pregs[PR_ERP] = env->pc; } void crisv10_cpu_do_interrupt(CPUState *cs) { cris_cpu_do_interrupt(cs); } bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { CRISCPU *cpu = CRIS_CPU(cs); cs->exception_index = 0xaa; cpu->env.pregs[PR_EDA] = address; cpu_loop_exit_restore(cs, retaddr); } #else /* !CONFIG_USER_ONLY */ static void cris_shift_ccs(CPUCRISState *env) { uint32_t ccs; /* Apply the ccs shift. */ ccs = env->pregs[PR_CCS]; ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff; env->pregs[PR_CCS] = ccs; } bool cris_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; struct cris_mmu_result res; int prot, miss; target_ulong phy; miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, access_type, mmu_idx, 0); if (likely(!miss)) { /* * Mask off the cache selection bit. The ETRAX busses do not * see the top bit. */ phy = res.phy & ~0x80000000; prot = res.prot; tlb_set_page(cs, address & TARGET_PAGE_MASK, phy, prot, mmu_idx, TARGET_PAGE_SIZE); return true; } if (probe) { return false; } if (cs->exception_index == EXCP_BUSFAULT) { cpu_abort(cs, "CRIS: Illegal recursive bus fault." "addr=%" VADDR_PRIx " access_type=%d\n", address, access_type); } env->pregs[PR_EDA] = address; cs->exception_index = EXCP_BUSFAULT; env->fault_vector = res.bf_vec; if (retaddr) { if (cpu_restore_state(cs, retaddr, true)) { /* Evaluate flags after retranslation. */ helper_top_evaluate_flags(env); } } cpu_loop_exit(cs); } void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { cris_cpu_tlb_fill(cs, addr, size, access_type, mmu_idx, false, retaddr); } void crisv10_cpu_do_interrupt(CPUState *cs) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", cs->exception_index, cs->interrupt_request); if (env->dslot) { /* CRISv10 never takes interrupts while in a delay-slot. */ cpu_abort(cs, "CRIS: Interrupt on delay-slot\n"); } assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); switch (cs->exception_index) { case EXCP_BREAK: /* These exceptions are genereated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PRV10_BRP] = env->pc; break; case EXCP_NMI: /* NMI is hardwired to vector zero. */ ex_vec = 0; env->pregs[PR_CCS] &= ~M_FLAG_V10; env->pregs[PRV10_BRP] = env->pc; break; case EXCP_BUSFAULT: cpu_abort(cs, "Unhandled busfault"); break; default: /* The interrupt controller gives us the vector. */ ex_vec = env->interrupt_vector; /* Normal interrupts are taken between TB's. env->pc is valid here. */ env->pregs[PR_ERP] = env->pc; break; } if (env->pregs[PR_CCS] & U_FLAG) { /* Swap stack pointers. */ env->pregs[PR_USP] = env->regs[R_SP]; env->regs[R_SP] = env->ksp; } /* Now that we are in kernel mode, load the handlers address. */ env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); env->locked_irq = 1; env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", __func__, env->pc, ex_vec, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_ERP]); } void cris_cpu_do_interrupt(CPUState *cs) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", cs->exception_index, cs->interrupt_request); switch (cs->exception_index) { case EXCP_BREAK: /* These exceptions are genereated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PR_ERP] = env->pc; break; case EXCP_NMI: /* NMI is hardwired to vector zero. */ ex_vec = 0; env->pregs[PR_CCS] &= ~M_FLAG_V32; env->pregs[PR_NRP] = env->pc; break; case EXCP_BUSFAULT: ex_vec = env->fault_vector; env->pregs[PR_ERP] = env->pc; break; default: /* The interrupt controller gives us the vector. */ ex_vec = env->interrupt_vector; /* Normal interrupts are taken between TB's. env->pc is valid here. */ env->pregs[PR_ERP] = env->pc; break; } /* Fill in the IDX field. */ env->pregs[PR_EXS] = (ex_vec & 0xff) << 8; if (env->dslot) { D_LOG("excp isr=%x PC=%x ds=%d SP=%x" " ERP=%x pid=%x ccs=%x cc=%d %x\n", ex_vec, env->pc, env->dslot, env->regs[R_SP], env->pregs[PR_ERP], env->pregs[PR_PID], env->pregs[PR_CCS], env->cc_op, env->cc_mask); /* We loose the btarget, btaken state here so rexec the branch. */ env->pregs[PR_ERP] -= env->dslot; /* Exception starts with dslot cleared. */ env->dslot = 0; } if (env->pregs[PR_CCS] & U_FLAG) { /* Swap stack pointers. */ env->pregs[PR_USP] = env->regs[R_SP]; env->regs[R_SP] = env->ksp; } /* Apply the CRIS CCS shift. Clears U if set. */ cris_shift_ccs(env); /* Now that we are in kernel mode, load the handlers address. This load may not fault, real hw leaves that behaviour as undefined. */ env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); /* Clear the excption_index to avoid spurios hw_aborts for recursive bus faults. */ cs->exception_index = -1; D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", __func__, env->pc, ex_vec, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_ERP]); } hwaddr cris_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { CRISCPU *cpu = CRIS_CPU(cs); uint32_t phy = addr; struct cris_mmu_result res; int miss; miss = cris_mmu_translate(&res, &cpu->env, addr, 0, 0, 1); /* If D TLB misses, try I TLB. */ if (miss) { miss = cris_mmu_translate(&res, &cpu->env, addr, 2, 0, 1); } if (!miss) { phy = res.phy; } D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); return phy; } #endif bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; bool ret = false; if (interrupt_request & CPU_INTERRUPT_HARD && (env->pregs[PR_CCS] & I_FLAG) && !env->locked_irq) { cs->exception_index = EXCP_IRQ; cc->do_interrupt(cs); ret = true; } if (interrupt_request & CPU_INTERRUPT_NMI) { unsigned int m_flag_archval; if (env->pregs[PR_VR] < 32) { m_flag_archval = M_FLAG_V10; } else { m_flag_archval = M_FLAG_V32; } if ((env->pregs[PR_CCS] & m_flag_archval)) { cs->exception_index = EXCP_NMI; cc->do_interrupt(cs); ret = true; } } return ret; }