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"
29 #include "qemu/main-loop.h"
31 #include "exec/helper-proto.h"
32 #include "qemu/host-utils.h"
33 #include "exec/exec-all.h"
35 void HELPER(exception
)(CPUXtensaState
*env
, uint32_t excp
)
37 CPUState
*cs
= env_cpu(env
);
39 cs
->exception_index
= excp
;
40 if (excp
== EXCP_YIELD
) {
41 env
->yield_needed
= 0;
43 if (excp
== EXCP_DEBUG
) {
44 env
->exception_taken
= 0;
49 void HELPER(exception_cause
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t cause
)
54 if (env
->sregs
[PS
] & PS_EXCM
) {
55 if (env
->config
->ndepc
) {
56 env
->sregs
[DEPC
] = pc
;
58 env
->sregs
[EPC1
] = pc
;
62 env
->sregs
[EPC1
] = pc
;
63 vector
= (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
66 env
->sregs
[EXCCAUSE
] = cause
;
67 env
->sregs
[PS
] |= PS_EXCM
;
69 HELPER(exception
)(env
, vector
);
72 void HELPER(exception_cause_vaddr
)(CPUXtensaState
*env
,
73 uint32_t pc
, uint32_t cause
, uint32_t vaddr
)
75 env
->sregs
[EXCVADDR
] = vaddr
;
76 HELPER(exception_cause
)(env
, pc
, cause
);
79 void debug_exception_env(CPUXtensaState
*env
, uint32_t cause
)
81 if (xtensa_get_cintlevel(env
) < env
->config
->debug_level
) {
82 HELPER(debug_exception
)(env
, env
->pc
, cause
);
86 void HELPER(debug_exception
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t cause
)
88 unsigned level
= env
->config
->debug_level
;
91 env
->sregs
[DEBUGCAUSE
] = cause
;
92 env
->sregs
[EPC1
+ level
- 1] = pc
;
93 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
94 env
->sregs
[PS
] = (env
->sregs
[PS
] & ~PS_INTLEVEL
) | PS_EXCM
|
95 (level
<< PS_INTLEVEL_SHIFT
);
96 HELPER(exception
)(env
, EXC_DEBUG
);
99 #ifndef CONFIG_USER_ONLY
101 void HELPER(waiti
)(CPUXtensaState
*env
, uint32_t pc
, uint32_t intlevel
)
103 CPUState
*cpu
= env_cpu(env
);
106 env
->sregs
[PS
] = (env
->sregs
[PS
] & ~PS_INTLEVEL
) |
107 (intlevel
<< PS_INTLEVEL_SHIFT
);
109 qemu_mutex_lock_iothread();
110 check_interrupts(env
);
111 qemu_mutex_unlock_iothread();
113 if (env
->pending_irq_level
) {
119 HELPER(exception
)(env
, EXCP_HLT
);
122 void HELPER(check_interrupts
)(CPUXtensaState
*env
)
124 qemu_mutex_lock_iothread();
125 check_interrupts(env
);
126 qemu_mutex_unlock_iothread();
129 void HELPER(intset
)(CPUXtensaState
*env
, uint32_t v
)
131 atomic_or(&env
->sregs
[INTSET
],
132 v
& env
->config
->inttype_mask
[INTTYPE_SOFTWARE
]);
135 static void intclear(CPUXtensaState
*env
, uint32_t v
)
137 atomic_and(&env
->sregs
[INTSET
], ~v
);
140 void HELPER(intclear
)(CPUXtensaState
*env
, uint32_t v
)
142 intclear(env
, v
& (env
->config
->inttype_mask
[INTTYPE_SOFTWARE
] |
143 env
->config
->inttype_mask
[INTTYPE_EDGE
]));
146 static uint32_t relocated_vector(CPUXtensaState
*env
, uint32_t vector
)
148 if (xtensa_option_enabled(env
->config
,
149 XTENSA_OPTION_RELOCATABLE_VECTOR
)) {
150 return vector
- env
->config
->vecbase
+ env
->sregs
[VECBASE
];
157 * Handle penging IRQ.
158 * For the high priority interrupt jump to the corresponding interrupt vector.
159 * For the level-1 interrupt convert it to either user, kernel or double
160 * exception with the 'level-1 interrupt' exception cause.
162 static void handle_interrupt(CPUXtensaState
*env
)
164 int level
= env
->pending_irq_level
;
166 if ((level
> xtensa_get_cintlevel(env
) &&
167 level
<= env
->config
->nlevel
&&
168 (env
->config
->level_mask
[level
] &
169 env
->sregs
[INTSET
] & env
->sregs
[INTENABLE
])) ||
170 level
== env
->config
->nmi_level
) {
171 CPUState
*cs
= env_cpu(env
);
174 env
->sregs
[EPC1
+ level
- 1] = env
->pc
;
175 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
177 (env
->sregs
[PS
] & ~PS_INTLEVEL
) | level
| PS_EXCM
;
178 env
->pc
= relocated_vector(env
,
179 env
->config
->interrupt_vector
[level
]);
180 if (level
== env
->config
->nmi_level
) {
181 intclear(env
, env
->config
->inttype_mask
[INTTYPE_NMI
]);
184 env
->sregs
[EXCCAUSE
] = LEVEL1_INTERRUPT_CAUSE
;
186 if (env
->sregs
[PS
] & PS_EXCM
) {
187 if (env
->config
->ndepc
) {
188 env
->sregs
[DEPC
] = env
->pc
;
190 env
->sregs
[EPC1
] = env
->pc
;
192 cs
->exception_index
= EXC_DOUBLE
;
194 env
->sregs
[EPC1
] = env
->pc
;
195 cs
->exception_index
=
196 (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
198 env
->sregs
[PS
] |= PS_EXCM
;
200 env
->exception_taken
= 1;
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
);
245 env
->exception_taken
= 1;
247 qemu_log_mask(CPU_LOG_INT
,
248 "%s(pc = %08x) bad exception_index: %d\n",
249 __func__
, env
->pc
, cs
->exception_index
);
257 qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
258 __func__
, env
->pc
, cs
->exception_index
);
261 check_interrupts(env
);
264 void xtensa_cpu_do_interrupt(CPUState
*cs
)
269 bool xtensa_cpu_exec_interrupt(CPUState
*cs
, int interrupt_request
)
271 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
272 cs
->exception_index
= EXC_IRQ
;
273 xtensa_cpu_do_interrupt(cs
);