18d918d65SMax Filippov /* 28d918d65SMax Filippov * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. 38d918d65SMax Filippov * All rights reserved. 48d918d65SMax Filippov * 58d918d65SMax Filippov * Redistribution and use in source and binary forms, with or without 68d918d65SMax Filippov * modification, are permitted provided that the following conditions are met: 78d918d65SMax Filippov * * Redistributions of source code must retain the above copyright 88d918d65SMax Filippov * notice, this list of conditions and the following disclaimer. 98d918d65SMax Filippov * * Redistributions in binary form must reproduce the above copyright 108d918d65SMax Filippov * notice, this list of conditions and the following disclaimer in the 118d918d65SMax Filippov * documentation and/or other materials provided with the distribution. 128d918d65SMax Filippov * * Neither the name of the Open Source and Linux Lab nor the 138d918d65SMax Filippov * names of its contributors may be used to endorse or promote products 148d918d65SMax Filippov * derived from this software without specific prior written permission. 158d918d65SMax Filippov * 168d918d65SMax Filippov * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 178d918d65SMax Filippov * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 188d918d65SMax Filippov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 198d918d65SMax Filippov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 208d918d65SMax Filippov * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 218d918d65SMax Filippov * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 228d918d65SMax Filippov * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 238d918d65SMax Filippov * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 248d918d65SMax Filippov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 258d918d65SMax Filippov * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 268d918d65SMax Filippov */ 278d918d65SMax Filippov 288d918d65SMax Filippov #include "qemu/osdep.h" 298d918d65SMax Filippov #include "qemu/main-loop.h" 308d918d65SMax Filippov #include "cpu.h" 318d918d65SMax Filippov #include "exec/helper-proto.h" 328d918d65SMax Filippov #include "qemu/host-utils.h" 338d918d65SMax Filippov #include "exec/exec-all.h" 348d918d65SMax Filippov 358d918d65SMax Filippov void HELPER(exception)(CPUXtensaState *env, uint32_t excp) 368d918d65SMax Filippov { 37*92fddfbdSRichard Henderson CPUState *cs = env_cpu(env); 388d918d65SMax Filippov 398d918d65SMax Filippov cs->exception_index = excp; 408d918d65SMax Filippov if (excp == EXCP_YIELD) { 418d918d65SMax Filippov env->yield_needed = 0; 428d918d65SMax Filippov } 438d918d65SMax Filippov if (excp == EXCP_DEBUG) { 448d918d65SMax Filippov env->exception_taken = 0; 458d918d65SMax Filippov } 468d918d65SMax Filippov cpu_loop_exit(cs); 478d918d65SMax Filippov } 488d918d65SMax Filippov 498d918d65SMax Filippov void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 508d918d65SMax Filippov { 518d918d65SMax Filippov uint32_t vector; 528d918d65SMax Filippov 538d918d65SMax Filippov env->pc = pc; 548d918d65SMax Filippov if (env->sregs[PS] & PS_EXCM) { 558d918d65SMax Filippov if (env->config->ndepc) { 568d918d65SMax Filippov env->sregs[DEPC] = pc; 578d918d65SMax Filippov } else { 588d918d65SMax Filippov env->sregs[EPC1] = pc; 598d918d65SMax Filippov } 608d918d65SMax Filippov vector = EXC_DOUBLE; 618d918d65SMax Filippov } else { 628d918d65SMax Filippov env->sregs[EPC1] = pc; 638d918d65SMax Filippov vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 648d918d65SMax Filippov } 658d918d65SMax Filippov 668d918d65SMax Filippov env->sregs[EXCCAUSE] = cause; 678d918d65SMax Filippov env->sregs[PS] |= PS_EXCM; 688d918d65SMax Filippov 698d918d65SMax Filippov HELPER(exception)(env, vector); 708d918d65SMax Filippov } 718d918d65SMax Filippov 728d918d65SMax Filippov void HELPER(exception_cause_vaddr)(CPUXtensaState *env, 738d918d65SMax Filippov uint32_t pc, uint32_t cause, uint32_t vaddr) 748d918d65SMax Filippov { 758d918d65SMax Filippov env->sregs[EXCVADDR] = vaddr; 768d918d65SMax Filippov HELPER(exception_cause)(env, pc, cause); 778d918d65SMax Filippov } 788d918d65SMax Filippov 798d918d65SMax Filippov void debug_exception_env(CPUXtensaState *env, uint32_t cause) 808d918d65SMax Filippov { 818d918d65SMax Filippov if (xtensa_get_cintlevel(env) < env->config->debug_level) { 828d918d65SMax Filippov HELPER(debug_exception)(env, env->pc, cause); 838d918d65SMax Filippov } 848d918d65SMax Filippov } 858d918d65SMax Filippov 868d918d65SMax Filippov void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause) 878d918d65SMax Filippov { 888d918d65SMax Filippov unsigned level = env->config->debug_level; 898d918d65SMax Filippov 908d918d65SMax Filippov env->pc = pc; 918d918d65SMax Filippov env->sregs[DEBUGCAUSE] = cause; 928d918d65SMax Filippov env->sregs[EPC1 + level - 1] = pc; 938d918d65SMax Filippov env->sregs[EPS2 + level - 2] = env->sregs[PS]; 948d918d65SMax Filippov env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | 958d918d65SMax Filippov (level << PS_INTLEVEL_SHIFT); 968d918d65SMax Filippov HELPER(exception)(env, EXC_DEBUG); 978d918d65SMax Filippov } 988d918d65SMax Filippov 998d918d65SMax Filippov #ifndef CONFIG_USER_ONLY 1008d918d65SMax Filippov 1018d918d65SMax Filippov void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) 1028d918d65SMax Filippov { 103*92fddfbdSRichard Henderson CPUState *cpu = env_cpu(env); 1048d918d65SMax Filippov 1058d918d65SMax Filippov env->pc = pc; 1068d918d65SMax Filippov env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | 1078d918d65SMax Filippov (intlevel << PS_INTLEVEL_SHIFT); 1088d918d65SMax Filippov 1098d918d65SMax Filippov qemu_mutex_lock_iothread(); 1108d918d65SMax Filippov check_interrupts(env); 1118d918d65SMax Filippov qemu_mutex_unlock_iothread(); 1128d918d65SMax Filippov 1138d918d65SMax Filippov if (env->pending_irq_level) { 114*92fddfbdSRichard Henderson cpu_loop_exit(cpu); 1158d918d65SMax Filippov return; 1168d918d65SMax Filippov } 1178d918d65SMax Filippov 1188d918d65SMax Filippov cpu->halted = 1; 1198d918d65SMax Filippov HELPER(exception)(env, EXCP_HLT); 1208d918d65SMax Filippov } 1218d918d65SMax Filippov 1228d918d65SMax Filippov void HELPER(check_interrupts)(CPUXtensaState *env) 1238d918d65SMax Filippov { 1248d918d65SMax Filippov qemu_mutex_lock_iothread(); 1258d918d65SMax Filippov check_interrupts(env); 1268d918d65SMax Filippov qemu_mutex_unlock_iothread(); 1278d918d65SMax Filippov } 1288d918d65SMax Filippov 129fa92bd4aSMax Filippov void HELPER(intset)(CPUXtensaState *env, uint32_t v) 130fa92bd4aSMax Filippov { 131fa92bd4aSMax Filippov atomic_or(&env->sregs[INTSET], 132fa92bd4aSMax Filippov v & env->config->inttype_mask[INTTYPE_SOFTWARE]); 133fa92bd4aSMax Filippov } 134fa92bd4aSMax Filippov 135fa92bd4aSMax Filippov void HELPER(intclear)(CPUXtensaState *env, uint32_t v) 136fa92bd4aSMax Filippov { 137fa92bd4aSMax Filippov atomic_and(&env->sregs[INTSET], 138fa92bd4aSMax Filippov ~(v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | 139fa92bd4aSMax Filippov env->config->inttype_mask[INTTYPE_EDGE]))); 140fa92bd4aSMax Filippov } 141fa92bd4aSMax Filippov 1428d918d65SMax Filippov static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) 1438d918d65SMax Filippov { 1448d918d65SMax Filippov if (xtensa_option_enabled(env->config, 1458d918d65SMax Filippov XTENSA_OPTION_RELOCATABLE_VECTOR)) { 1468d918d65SMax Filippov return vector - env->config->vecbase + env->sregs[VECBASE]; 1478d918d65SMax Filippov } else { 1488d918d65SMax Filippov return vector; 1498d918d65SMax Filippov } 1508d918d65SMax Filippov } 1518d918d65SMax Filippov 1528d918d65SMax Filippov /*! 1538d918d65SMax Filippov * Handle penging IRQ. 1548d918d65SMax Filippov * For the high priority interrupt jump to the corresponding interrupt vector. 1558d918d65SMax Filippov * For the level-1 interrupt convert it to either user, kernel or double 1568d918d65SMax Filippov * exception with the 'level-1 interrupt' exception cause. 1578d918d65SMax Filippov */ 1588d918d65SMax Filippov static void handle_interrupt(CPUXtensaState *env) 1598d918d65SMax Filippov { 1608d918d65SMax Filippov int level = env->pending_irq_level; 1618d918d65SMax Filippov 1628d918d65SMax Filippov if (level > xtensa_get_cintlevel(env) && 1638d918d65SMax Filippov level <= env->config->nlevel && 1648d918d65SMax Filippov (env->config->level_mask[level] & 1658d918d65SMax Filippov env->sregs[INTSET] & 1668d918d65SMax Filippov env->sregs[INTENABLE])) { 167*92fddfbdSRichard Henderson CPUState *cs = env_cpu(env); 1688d918d65SMax Filippov 1698d918d65SMax Filippov if (level > 1) { 1708d918d65SMax Filippov env->sregs[EPC1 + level - 1] = env->pc; 1718d918d65SMax Filippov env->sregs[EPS2 + level - 2] = env->sregs[PS]; 1728d918d65SMax Filippov env->sregs[PS] = 1738d918d65SMax Filippov (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; 1748d918d65SMax Filippov env->pc = relocated_vector(env, 1758d918d65SMax Filippov env->config->interrupt_vector[level]); 1768d918d65SMax Filippov } else { 1778d918d65SMax Filippov env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; 1788d918d65SMax Filippov 1798d918d65SMax Filippov if (env->sregs[PS] & PS_EXCM) { 1808d918d65SMax Filippov if (env->config->ndepc) { 1818d918d65SMax Filippov env->sregs[DEPC] = env->pc; 1828d918d65SMax Filippov } else { 1838d918d65SMax Filippov env->sregs[EPC1] = env->pc; 1848d918d65SMax Filippov } 1858d918d65SMax Filippov cs->exception_index = EXC_DOUBLE; 1868d918d65SMax Filippov } else { 1878d918d65SMax Filippov env->sregs[EPC1] = env->pc; 1888d918d65SMax Filippov cs->exception_index = 1898d918d65SMax Filippov (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; 1908d918d65SMax Filippov } 1918d918d65SMax Filippov env->sregs[PS] |= PS_EXCM; 1928d918d65SMax Filippov } 1938d918d65SMax Filippov env->exception_taken = 1; 1948d918d65SMax Filippov } 1958d918d65SMax Filippov } 1968d918d65SMax Filippov 1978d918d65SMax Filippov /* Called from cpu_handle_interrupt with BQL held */ 1988d918d65SMax Filippov void xtensa_cpu_do_interrupt(CPUState *cs) 1998d918d65SMax Filippov { 2008d918d65SMax Filippov XtensaCPU *cpu = XTENSA_CPU(cs); 2018d918d65SMax Filippov CPUXtensaState *env = &cpu->env; 2028d918d65SMax Filippov 2038d918d65SMax Filippov if (cs->exception_index == EXC_IRQ) { 2048d918d65SMax Filippov qemu_log_mask(CPU_LOG_INT, 2058d918d65SMax Filippov "%s(EXC_IRQ) level = %d, cintlevel = %d, " 2068d918d65SMax Filippov "pc = %08x, a0 = %08x, ps = %08x, " 2078d918d65SMax Filippov "intset = %08x, intenable = %08x, " 2088d918d65SMax Filippov "ccount = %08x\n", 2098d918d65SMax Filippov __func__, env->pending_irq_level, 2108d918d65SMax Filippov xtensa_get_cintlevel(env), 2118d918d65SMax Filippov env->pc, env->regs[0], env->sregs[PS], 2128d918d65SMax Filippov env->sregs[INTSET], env->sregs[INTENABLE], 2138d918d65SMax Filippov env->sregs[CCOUNT]); 2148d918d65SMax Filippov handle_interrupt(env); 2158d918d65SMax Filippov } 2168d918d65SMax Filippov 2178d918d65SMax Filippov switch (cs->exception_index) { 2188d918d65SMax Filippov case EXC_WINDOW_OVERFLOW4: 2198d918d65SMax Filippov case EXC_WINDOW_UNDERFLOW4: 2208d918d65SMax Filippov case EXC_WINDOW_OVERFLOW8: 2218d918d65SMax Filippov case EXC_WINDOW_UNDERFLOW8: 2228d918d65SMax Filippov case EXC_WINDOW_OVERFLOW12: 2238d918d65SMax Filippov case EXC_WINDOW_UNDERFLOW12: 2248d918d65SMax Filippov case EXC_KERNEL: 2258d918d65SMax Filippov case EXC_USER: 2268d918d65SMax Filippov case EXC_DOUBLE: 2278d918d65SMax Filippov case EXC_DEBUG: 2288d918d65SMax Filippov qemu_log_mask(CPU_LOG_INT, "%s(%d) " 2298d918d65SMax Filippov "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", 2308d918d65SMax Filippov __func__, cs->exception_index, 2318d918d65SMax Filippov env->pc, env->regs[0], env->sregs[PS], 2328d918d65SMax Filippov env->sregs[CCOUNT]); 2338d918d65SMax Filippov if (env->config->exception_vector[cs->exception_index]) { 2348d918d65SMax Filippov uint32_t vector; 2358d918d65SMax Filippov 2368d918d65SMax Filippov vector = env->config->exception_vector[cs->exception_index]; 2378d918d65SMax Filippov env->pc = relocated_vector(env, vector); 2388d918d65SMax Filippov env->exception_taken = 1; 2398d918d65SMax Filippov } else { 2408d918d65SMax Filippov qemu_log_mask(CPU_LOG_INT, 2418d918d65SMax Filippov "%s(pc = %08x) bad exception_index: %d\n", 2428d918d65SMax Filippov __func__, env->pc, cs->exception_index); 2438d918d65SMax Filippov } 2448d918d65SMax Filippov break; 2458d918d65SMax Filippov 2468d918d65SMax Filippov case EXC_IRQ: 2478d918d65SMax Filippov break; 2488d918d65SMax Filippov 2498d918d65SMax Filippov default: 2508d918d65SMax Filippov qemu_log("%s(pc = %08x) unknown exception_index: %d\n", 2518d918d65SMax Filippov __func__, env->pc, cs->exception_index); 2528d918d65SMax Filippov break; 2538d918d65SMax Filippov } 2548d918d65SMax Filippov check_interrupts(env); 2558d918d65SMax Filippov } 2568d918d65SMax Filippov #else 2578d918d65SMax Filippov void xtensa_cpu_do_interrupt(CPUState *cs) 2588d918d65SMax Filippov { 2598d918d65SMax Filippov } 2608d918d65SMax Filippov #endif 2618d918d65SMax Filippov 2628d918d65SMax Filippov bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 2638d918d65SMax Filippov { 2648d918d65SMax Filippov if (interrupt_request & CPU_INTERRUPT_HARD) { 2658d918d65SMax Filippov cs->exception_index = EXC_IRQ; 2668d918d65SMax Filippov xtensa_cpu_do_interrupt(cs); 2678d918d65SMax Filippov return true; 2688d918d65SMax Filippov } 2698d918d65SMax Filippov return false; 2708d918d65SMax Filippov } 271