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