2 * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the Open Source and Linux Lab nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "qemu/osdep.h"
30 #include "qemu/main-loop.h"
32 #include "exec/helper-proto.h"
33 #include "qemu/host-utils.h"
34 #include "exec/exec-all.h"
36 void HELPER(exception
)(CPUXtensaState
*env
, uint32_t excp
)
38 CPUState
*cs
= env_cpu(env
);
40 cs
->exception_index
= excp
;
41 if (excp
== EXCP_YIELD
) {
42 env
->yield_needed
= 0;
47 void HELPER(exception_cause
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t cause
)
52 if (env
->sregs
[PS
] & PS_EXCM
) {
53 if (env
->config
->ndepc
) {
54 env
->sregs
[DEPC
] = pc
;
56 env
->sregs
[EPC1
] = pc
;
60 env
->sregs
[EPC1
] = pc
;
61 vector
= (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
64 env
->sregs
[EXCCAUSE
] = cause
;
65 env
->sregs
[PS
] |= PS_EXCM
;
67 HELPER(exception
)(env
, vector
);
70 void HELPER(exception_cause_vaddr
)(CPUXtensaState
*env
,
71 uint32_t pc
, uint32_t cause
, uint32_t vaddr
)
73 env
->sregs
[EXCVADDR
] = vaddr
;
74 HELPER(exception_cause
)(env
, pc
, cause
);
77 void debug_exception_env(CPUXtensaState
*env
, uint32_t cause
)
79 if (xtensa_get_cintlevel(env
) < env
->config
->debug_level
) {
80 HELPER(debug_exception
)(env
, env
->pc
, cause
);
84 void HELPER(debug_exception
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t cause
)
86 unsigned level
= env
->config
->debug_level
;
89 env
->sregs
[DEBUGCAUSE
] = cause
;
90 env
->sregs
[EPC1
+ level
- 1] = pc
;
91 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
92 env
->sregs
[PS
] = (env
->sregs
[PS
] & ~PS_INTLEVEL
) | PS_EXCM
|
93 (level
<< PS_INTLEVEL_SHIFT
);
94 HELPER(exception
)(env
, EXC_DEBUG
);
97 #ifndef CONFIG_USER_ONLY
99 void HELPER(waiti
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t intlevel
)
101 CPUState
*cpu
= env_cpu(env
);
104 env
->sregs
[PS
] = (env
->sregs
[PS
] & ~PS_INTLEVEL
) |
105 (intlevel
<< PS_INTLEVEL_SHIFT
);
107 qemu_mutex_lock_iothread();
108 check_interrupts(env
);
109 qemu_mutex_unlock_iothread();
111 if (env
->pending_irq_level
) {
117 HELPER(exception
)(env
, EXCP_HLT
);
120 void HELPER(check_interrupts
)(CPUXtensaState
*env
)
122 qemu_mutex_lock_iothread();
123 check_interrupts(env
);
124 qemu_mutex_unlock_iothread();
127 void HELPER(intset
)(CPUXtensaState
*env
, uint32_t v
)
129 qatomic_or(&env
->sregs
[INTSET
],
130 v
& env
->config
->inttype_mask
[INTTYPE_SOFTWARE
]);
133 static void intclear(CPUXtensaState
*env
, uint32_t v
)
135 qatomic_and(&env
->sregs
[INTSET
], ~v
);
138 void HELPER(intclear
)(CPUXtensaState
*env
, uint32_t v
)
140 intclear(env
, v
& (env
->config
->inttype_mask
[INTTYPE_SOFTWARE
] |
141 env
->config
->inttype_mask
[INTTYPE_EDGE
]));
144 static uint32_t relocated_vector(CPUXtensaState
*env
, uint32_t vector
)
146 if (xtensa_option_enabled(env
->config
,
147 XTENSA_OPTION_RELOCATABLE_VECTOR
)) {
148 return vector
- env
->config
->vecbase
+ env
->sregs
[VECBASE
];
155 * Handle penging IRQ.
156 * For the high priority interrupt jump to the corresponding interrupt vector.
157 * For the level-1 interrupt convert it to either user, kernel or double
158 * exception with the 'level-1 interrupt' exception cause.
160 static void handle_interrupt(CPUXtensaState
*env
)
162 int level
= env
->pending_irq_level
;
164 if ((level
> xtensa_get_cintlevel(env
) &&
165 level
<= env
->config
->nlevel
&&
166 (env
->config
->level_mask
[level
] &
167 env
->sregs
[INTSET
] & env
->sregs
[INTENABLE
])) ||
168 level
== env
->config
->nmi_level
) {
169 CPUState
*cs
= env_cpu(env
);
172 /* env->config->nlevel check should have ensured this */
173 assert(level
< sizeof(env
->config
->interrupt_vector
));
175 env
->sregs
[EPC1
+ level
- 1] = env
->pc
;
176 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
178 (env
->sregs
[PS
] & ~PS_INTLEVEL
) | level
| PS_EXCM
;
179 env
->pc
= relocated_vector(env
,
180 env
->config
->interrupt_vector
[level
]);
181 if (level
== env
->config
->nmi_level
) {
182 intclear(env
, env
->config
->inttype_mask
[INTTYPE_NMI
]);
185 env
->sregs
[EXCCAUSE
] = LEVEL1_INTERRUPT_CAUSE
;
187 if (env
->sregs
[PS
] & PS_EXCM
) {
188 if (env
->config
->ndepc
) {
189 env
->sregs
[DEPC
] = env
->pc
;
191 env
->sregs
[EPC1
] = env
->pc
;
193 cs
->exception_index
= EXC_DOUBLE
;
195 env
->sregs
[EPC1
] = env
->pc
;
196 cs
->exception_index
=
197 (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
199 env
->sregs
[PS
] |= PS_EXCM
;
204 /* Called from cpu_handle_interrupt with BQL held */
205 void xtensa_cpu_do_interrupt(CPUState
*cs
)
207 XtensaCPU
*cpu
= XTENSA_CPU(cs
);
208 CPUXtensaState
*env
= &cpu
->env
;
210 if (cs
->exception_index
== EXC_IRQ
) {
211 qemu_log_mask(CPU_LOG_INT
,
212 "%s(EXC_IRQ) level = %d, cintlevel = %d, "
213 "pc = %08x, a0 = %08x, ps = %08x, "
214 "intset = %08x, intenable = %08x, "
216 __func__
, env
->pending_irq_level
,
217 xtensa_get_cintlevel(env
),
218 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
219 env
->sregs
[INTSET
], env
->sregs
[INTENABLE
],
221 handle_interrupt(env
);
224 switch (cs
->exception_index
) {
225 case EXC_WINDOW_OVERFLOW4
:
226 case EXC_WINDOW_UNDERFLOW4
:
227 case EXC_WINDOW_OVERFLOW8
:
228 case EXC_WINDOW_UNDERFLOW8
:
229 case EXC_WINDOW_OVERFLOW12
:
230 case EXC_WINDOW_UNDERFLOW12
:
235 qemu_log_mask(CPU_LOG_INT
, "%s(%d) "
236 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
237 __func__
, cs
->exception_index
,
238 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
240 if (env
->config
->exception_vector
[cs
->exception_index
]) {
243 vector
= env
->config
->exception_vector
[cs
->exception_index
];
244 env
->pc
= relocated_vector(env
, vector
);
246 qemu_log_mask(CPU_LOG_INT
,
247 "%s(pc = %08x) bad exception_index: %d\n",
248 __func__
, env
->pc
, cs
->exception_index
);
256 qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
257 __func__
, env
->pc
, cs
->exception_index
);
260 check_interrupts(env
);
263 bool xtensa_cpu_exec_interrupt(CPUState
*cs
, int interrupt_request
)
265 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
266 cs
->exception_index
= EXC_IRQ
;
267 xtensa_cpu_do_interrupt(cs
);
273 #endif /* !CONFIG_USER_ONLY */