xref: /qemu/target/xtensa/exc_helper.c (revision 7cef6d686309e2792186504ae17cf4f3eb57ef68)
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"
29cd617484SPhilippe Mathieu-Daudé #include "qemu/log.h"
308d918d65SMax Filippov #include "qemu/main-loop.h"
318d918d65SMax Filippov #include "cpu.h"
328d918d65SMax Filippov #include "exec/helper-proto.h"
338d918d65SMax Filippov #include "qemu/host-utils.h"
34b8be0524SPhilippe Mathieu-Daudé #include "qemu/atomic.h"
358d918d65SMax Filippov 
HELPER(exception)368d918d65SMax Filippov void HELPER(exception)(CPUXtensaState *env, uint32_t excp)
378d918d65SMax Filippov {
3892fddfbdSRichard Henderson     CPUState *cs = env_cpu(env);
398d918d65SMax Filippov 
408d918d65SMax Filippov     cs->exception_index = excp;
418d918d65SMax Filippov     if (excp == EXCP_YIELD) {
428d918d65SMax Filippov         env->yield_needed = 0;
438d918d65SMax Filippov     }
448d918d65SMax Filippov     cpu_loop_exit(cs);
458d918d65SMax Filippov }
468d918d65SMax Filippov 
HELPER(exception_cause)478d918d65SMax Filippov void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause)
488d918d65SMax Filippov {
498d918d65SMax Filippov     uint32_t vector;
508d918d65SMax Filippov 
518d918d65SMax Filippov     env->pc = pc;
528d918d65SMax Filippov     if (env->sregs[PS] & PS_EXCM) {
538d918d65SMax Filippov         if (env->config->ndepc) {
548d918d65SMax Filippov             env->sregs[DEPC] = pc;
558d918d65SMax Filippov         } else {
568d918d65SMax Filippov             env->sregs[EPC1] = pc;
578d918d65SMax Filippov         }
588d918d65SMax Filippov         vector = EXC_DOUBLE;
598d918d65SMax Filippov     } else {
608d918d65SMax Filippov         env->sregs[EPC1] = pc;
618d918d65SMax Filippov         vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL;
628d918d65SMax Filippov     }
638d918d65SMax Filippov 
648d918d65SMax Filippov     env->sregs[EXCCAUSE] = cause;
658d918d65SMax Filippov     env->sregs[PS] |= PS_EXCM;
668d918d65SMax Filippov 
678d918d65SMax Filippov     HELPER(exception)(env, vector);
688d918d65SMax Filippov }
698d918d65SMax Filippov 
HELPER(exception_cause_vaddr)708d918d65SMax Filippov void HELPER(exception_cause_vaddr)(CPUXtensaState *env,
718d918d65SMax Filippov                                    uint32_t pc, uint32_t cause, uint32_t vaddr)
728d918d65SMax Filippov {
738d918d65SMax Filippov     env->sregs[EXCVADDR] = vaddr;
748d918d65SMax Filippov     HELPER(exception_cause)(env, pc, cause);
758d918d65SMax Filippov }
768d918d65SMax Filippov 
debug_exception_env(CPUXtensaState * env,uint32_t cause)778d918d65SMax Filippov void debug_exception_env(CPUXtensaState *env, uint32_t cause)
788d918d65SMax Filippov {
798d918d65SMax Filippov     if (xtensa_get_cintlevel(env) < env->config->debug_level) {
808d918d65SMax Filippov         HELPER(debug_exception)(env, env->pc, cause);
818d918d65SMax Filippov     }
828d918d65SMax Filippov }
838d918d65SMax Filippov 
HELPER(debug_exception)848d918d65SMax Filippov void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause)
858d918d65SMax Filippov {
868d918d65SMax Filippov     unsigned level = env->config->debug_level;
878d918d65SMax Filippov 
888d918d65SMax Filippov     env->pc = pc;
898d918d65SMax Filippov     env->sregs[DEBUGCAUSE] = cause;
908d918d65SMax Filippov     env->sregs[EPC1 + level - 1] = pc;
918d918d65SMax Filippov     env->sregs[EPS2 + level - 2] = env->sregs[PS];
928d918d65SMax Filippov     env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM |
938d918d65SMax Filippov         (level << PS_INTLEVEL_SHIFT);
948d918d65SMax Filippov     HELPER(exception)(env, EXC_DEBUG);
958d918d65SMax Filippov }
968d918d65SMax Filippov 
978d918d65SMax Filippov #ifndef CONFIG_USER_ONLY
988d918d65SMax Filippov 
HELPER(waiti)998d918d65SMax Filippov void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel)
1008d918d65SMax Filippov {
10192fddfbdSRichard Henderson     CPUState *cpu = env_cpu(env);
1028d918d65SMax Filippov 
1038d918d65SMax Filippov     env->pc = pc;
1048d918d65SMax Filippov     env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) |
1058d918d65SMax Filippov         (intlevel << PS_INTLEVEL_SHIFT);
1068d918d65SMax Filippov 
107195801d7SStefan Hajnoczi     bql_lock();
1088d918d65SMax Filippov     check_interrupts(env);
109195801d7SStefan Hajnoczi     bql_unlock();
1108d918d65SMax Filippov 
1118d918d65SMax Filippov     if (env->pending_irq_level) {
11292fddfbdSRichard Henderson         cpu_loop_exit(cpu);
1138d918d65SMax Filippov         return;
1148d918d65SMax Filippov     }
1158d918d65SMax Filippov 
1168d918d65SMax Filippov     cpu->halted = 1;
1178d918d65SMax Filippov     HELPER(exception)(env, EXCP_HLT);
1188d918d65SMax Filippov }
1198d918d65SMax Filippov 
HELPER(check_interrupts)1208d918d65SMax Filippov void HELPER(check_interrupts)(CPUXtensaState *env)
1218d918d65SMax Filippov {
122195801d7SStefan Hajnoczi     bql_lock();
1238d918d65SMax Filippov     check_interrupts(env);
124195801d7SStefan Hajnoczi     bql_unlock();
1258d918d65SMax Filippov }
1268d918d65SMax Filippov 
HELPER(intset)127fa92bd4aSMax Filippov void HELPER(intset)(CPUXtensaState *env, uint32_t v)
128fa92bd4aSMax Filippov {
129d73415a3SStefan Hajnoczi     qatomic_or(&env->sregs[INTSET],
130fa92bd4aSMax Filippov               v & env->config->inttype_mask[INTTYPE_SOFTWARE]);
131fa92bd4aSMax Filippov }
132fa92bd4aSMax Filippov 
intclear(CPUXtensaState * env,uint32_t v)133a7d479eeSMax Filippov static void intclear(CPUXtensaState *env, uint32_t v)
134a7d479eeSMax Filippov {
135d73415a3SStefan Hajnoczi     qatomic_and(&env->sregs[INTSET], ~v);
136a7d479eeSMax Filippov }
137a7d479eeSMax Filippov 
HELPER(intclear)138fa92bd4aSMax Filippov void HELPER(intclear)(CPUXtensaState *env, uint32_t v)
139fa92bd4aSMax Filippov {
140a7d479eeSMax Filippov     intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] |
141a7d479eeSMax Filippov                        env->config->inttype_mask[INTTYPE_EDGE]));
142fa92bd4aSMax Filippov }
143fa92bd4aSMax Filippov 
relocated_vector(CPUXtensaState * env,uint32_t vector)1448d918d65SMax Filippov static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
1458d918d65SMax Filippov {
1468d918d65SMax Filippov     if (xtensa_option_enabled(env->config,
1478d918d65SMax Filippov                               XTENSA_OPTION_RELOCATABLE_VECTOR)) {
1488d918d65SMax Filippov         return vector - env->config->vecbase + env->sregs[VECBASE];
1498d918d65SMax Filippov     } else {
1508d918d65SMax Filippov         return vector;
1518d918d65SMax Filippov     }
1528d918d65SMax Filippov }
1538d918d65SMax Filippov 
1548d918d65SMax Filippov /*!
1558d918d65SMax Filippov  * Handle penging IRQ.
1568d918d65SMax Filippov  * For the high priority interrupt jump to the corresponding interrupt vector.
1578d918d65SMax Filippov  * For the level-1 interrupt convert it to either user, kernel or double
1588d918d65SMax Filippov  * exception with the 'level-1 interrupt' exception cause.
1598d918d65SMax Filippov  */
handle_interrupt(CPUXtensaState * env)1608d918d65SMax Filippov static void handle_interrupt(CPUXtensaState *env)
1618d918d65SMax Filippov {
1628d918d65SMax Filippov     int level = env->pending_irq_level;
1638d918d65SMax Filippov 
164a7d479eeSMax Filippov     if ((level > xtensa_get_cintlevel(env) &&
1658d918d65SMax Filippov          level <= env->config->nlevel &&
1668d918d65SMax Filippov          (env->config->level_mask[level] &
167a7d479eeSMax Filippov           env->sregs[INTSET] & env->sregs[INTENABLE])) ||
168a7d479eeSMax Filippov         level == env->config->nmi_level) {
16992fddfbdSRichard Henderson         CPUState *cs = env_cpu(env);
1708d918d65SMax Filippov 
1718d918d65SMax Filippov         if (level > 1) {
172ad18376bSPeter Maydell             /* env->config->nlevel check should have ensured this */
173*5e8e4f09SPeter Maydell             assert(level < ARRAY_SIZE(env->config->interrupt_vector));
174ad18376bSPeter Maydell 
1758d918d65SMax Filippov             env->sregs[EPC1 + level - 1] = env->pc;
1768d918d65SMax Filippov             env->sregs[EPS2 + level - 2] = env->sregs[PS];
1778d918d65SMax Filippov             env->sregs[PS] =
1788d918d65SMax Filippov                 (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM;
1798d918d65SMax Filippov             env->pc = relocated_vector(env,
1808d918d65SMax Filippov                                        env->config->interrupt_vector[level]);
181a7d479eeSMax Filippov             if (level == env->config->nmi_level) {
182a7d479eeSMax Filippov                 intclear(env, env->config->inttype_mask[INTTYPE_NMI]);
183a7d479eeSMax Filippov             }
1848d918d65SMax Filippov         } else {
1858d918d65SMax Filippov             env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE;
1868d918d65SMax Filippov 
1878d918d65SMax Filippov             if (env->sregs[PS] & PS_EXCM) {
1888d918d65SMax Filippov                 if (env->config->ndepc) {
1898d918d65SMax Filippov                     env->sregs[DEPC] = env->pc;
1908d918d65SMax Filippov                 } else {
1918d918d65SMax Filippov                     env->sregs[EPC1] = env->pc;
1928d918d65SMax Filippov                 }
1938d918d65SMax Filippov                 cs->exception_index = EXC_DOUBLE;
1948d918d65SMax Filippov             } else {
1958d918d65SMax Filippov                 env->sregs[EPC1] = env->pc;
1968d918d65SMax Filippov                 cs->exception_index =
1978d918d65SMax Filippov                     (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL;
1988d918d65SMax Filippov             }
1998d918d65SMax Filippov             env->sregs[PS] |= PS_EXCM;
2008d918d65SMax Filippov         }
2018d918d65SMax Filippov     }
2028d918d65SMax Filippov }
2038d918d65SMax Filippov 
2048d918d65SMax Filippov /* Called from cpu_handle_interrupt with BQL held */
xtensa_cpu_do_interrupt(CPUState * cs)2058d918d65SMax Filippov void xtensa_cpu_do_interrupt(CPUState *cs)
2068d918d65SMax Filippov {
20752049266SPhilippe Mathieu-Daudé     CPUXtensaState *env = cpu_env(cs);
2088d918d65SMax Filippov 
2098d918d65SMax Filippov     if (cs->exception_index == EXC_IRQ) {
2108d918d65SMax Filippov         qemu_log_mask(CPU_LOG_INT,
2118d918d65SMax Filippov                       "%s(EXC_IRQ) level = %d, cintlevel = %d, "
2128d918d65SMax Filippov                       "pc = %08x, a0 = %08x, ps = %08x, "
2138d918d65SMax Filippov                       "intset = %08x, intenable = %08x, "
2148d918d65SMax Filippov                       "ccount = %08x\n",
2158d918d65SMax Filippov                       __func__, env->pending_irq_level,
2168d918d65SMax Filippov                       xtensa_get_cintlevel(env),
2178d918d65SMax Filippov                       env->pc, env->regs[0], env->sregs[PS],
2188d918d65SMax Filippov                       env->sregs[INTSET], env->sregs[INTENABLE],
2198d918d65SMax Filippov                       env->sregs[CCOUNT]);
2208d918d65SMax Filippov         handle_interrupt(env);
2218d918d65SMax Filippov     }
2228d918d65SMax Filippov 
2238d918d65SMax Filippov     switch (cs->exception_index) {
2248d918d65SMax Filippov     case EXC_WINDOW_OVERFLOW4:
2258d918d65SMax Filippov     case EXC_WINDOW_UNDERFLOW4:
2268d918d65SMax Filippov     case EXC_WINDOW_OVERFLOW8:
2278d918d65SMax Filippov     case EXC_WINDOW_UNDERFLOW8:
2288d918d65SMax Filippov     case EXC_WINDOW_OVERFLOW12:
2298d918d65SMax Filippov     case EXC_WINDOW_UNDERFLOW12:
2308d918d65SMax Filippov     case EXC_KERNEL:
2318d918d65SMax Filippov     case EXC_USER:
2328d918d65SMax Filippov     case EXC_DOUBLE:
2338d918d65SMax Filippov     case EXC_DEBUG:
2348d918d65SMax Filippov         qemu_log_mask(CPU_LOG_INT, "%s(%d) "
2358d918d65SMax Filippov                       "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
2368d918d65SMax Filippov                       __func__, cs->exception_index,
2378d918d65SMax Filippov                       env->pc, env->regs[0], env->sregs[PS],
2388d918d65SMax Filippov                       env->sregs[CCOUNT]);
2398d918d65SMax Filippov         if (env->config->exception_vector[cs->exception_index]) {
2408d918d65SMax Filippov             uint32_t vector;
2418d918d65SMax Filippov 
2428d918d65SMax Filippov             vector = env->config->exception_vector[cs->exception_index];
2438d918d65SMax Filippov             env->pc = relocated_vector(env, vector);
2448d918d65SMax Filippov         } else {
2458d918d65SMax Filippov             qemu_log_mask(CPU_LOG_INT,
2468d918d65SMax Filippov                           "%s(pc = %08x) bad exception_index: %d\n",
2478d918d65SMax Filippov                           __func__, env->pc, cs->exception_index);
2488d918d65SMax Filippov         }
2498d918d65SMax Filippov         break;
2508d918d65SMax Filippov 
2518d918d65SMax Filippov     case EXC_IRQ:
2528d918d65SMax Filippov         break;
2538d918d65SMax Filippov 
2548d918d65SMax Filippov     default:
2558d918d65SMax Filippov         qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
2568d918d65SMax Filippov                  __func__, env->pc, cs->exception_index);
2578d918d65SMax Filippov         break;
2588d918d65SMax Filippov     }
2598d918d65SMax Filippov     check_interrupts(env);
2608d918d65SMax Filippov }
2618d918d65SMax Filippov 
xtensa_cpu_exec_interrupt(CPUState * cs,int interrupt_request)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 }
271f364a7f9SPhilippe Mathieu-Daudé 
272f364a7f9SPhilippe Mathieu-Daudé #endif /* !CONFIG_USER_ONLY */
273