| /* | 
 |  *  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 <http://www.gnu.org/licenses/>. | 
 |  */ | 
 |  | 
 | #include "cpu.h" | 
 | #include "mmu.h" | 
 | #include "qemu/host-utils.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; | 
 |  | 
 |     env->exception_index = -1; | 
 |     env->pregs[PR_ERP] = env->pc; | 
 | } | 
 |  | 
 | void crisv10_cpu_do_interrupt(CPUState *cs) | 
 | { | 
 |     cris_cpu_do_interrupt(cs); | 
 | } | 
 |  | 
 | int cpu_cris_handle_mmu_fault(CPUCRISState * env, target_ulong address, int rw, | 
 |                               int mmu_idx) | 
 | { | 
 |     CRISCPU *cpu = cris_env_get_cpu(env); | 
 |  | 
 |     env->exception_index = 0xaa; | 
 |     env->pregs[PR_EDA] = address; | 
 |     cpu_dump_state(CPU(cpu), stderr, fprintf, 0); | 
 |     return 1; | 
 | } | 
 |  | 
 | #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; | 
 | } | 
 |  | 
 | int cpu_cris_handle_mmu_fault(CPUCRISState *env, target_ulong address, int rw, | 
 |                               int mmu_idx) | 
 | { | 
 |     D(CPUState *cpu = CPU(cris_env_get_cpu(env))); | 
 |     struct cris_mmu_result res; | 
 |     int prot, miss; | 
 |     int r = -1; | 
 |     target_ulong phy; | 
 |  | 
 |     D(printf("%s addr=%x pc=%x rw=%x\n", __func__, address, env->pc, rw)); | 
 |     miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, | 
 |                               rw, mmu_idx, 0); | 
 |     if (miss) { | 
 |         if (env->exception_index == EXCP_BUSFAULT) { | 
 |             cpu_abort(env, | 
 |                       "CRIS: Illegal recursive bus fault." | 
 |                       "addr=%x rw=%d\n", | 
 |                       address, rw); | 
 |         } | 
 |  | 
 |         env->pregs[PR_EDA] = address; | 
 |         env->exception_index = EXCP_BUSFAULT; | 
 |         env->fault_vector = res.bf_vec; | 
 |         r = 1; | 
 |     } else { | 
 |         /* | 
 |          * 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(env, address & TARGET_PAGE_MASK, phy, | 
 |                      prot, mmu_idx, TARGET_PAGE_SIZE); | 
 |         r = 0; | 
 |     } | 
 |     if (r > 0) { | 
 |         D_LOG("%s returns %d irqreq=%x addr=%x phy=%x vec=%x pc=%x\n", | 
 |               __func__, r, cpu->interrupt_request, address, res.phy, | 
 |               res.bf_vec, env->pc); | 
 |     } | 
 |     return r; | 
 | } | 
 |  | 
 | 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", | 
 |           env->exception_index, | 
 |           cs->interrupt_request); | 
 |  | 
 |     assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); | 
 |     switch (env->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(env, "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", | 
 |           env->exception_index, | 
 |           cs->interrupt_request); | 
 |  | 
 |     switch (env->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.  */ | 
 |     env->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 |