xref: /qemu/target/xtensa/exc_helper.c (revision 92fddfbd1792cb6009f869c47e7ea55a741fe2e3)
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