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
= CPU(xtensa_env_get_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
)
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
) {
114 cpu_loop_exit(CPU(xtensa_env_get_cpu(env
)));
118 cpu
= CPU(xtensa_env_get_cpu(env
));
120 HELPER(exception
)(env
, EXCP_HLT
);
123 void HELPER(check_interrupts
)(CPUXtensaState
*env
)
125 qemu_mutex_lock_iothread();
126 check_interrupts(env
);
127 qemu_mutex_unlock_iothread();
130 void HELPER(intset
)(CPUXtensaState
*env
, uint32_t v
)
132 atomic_or(&env
->sregs
[INTSET
],
133 v
& env
->config
->inttype_mask
[INTTYPE_SOFTWARE
]);
136 void HELPER(intclear
)(CPUXtensaState
*env
, uint32_t v
)
138 atomic_and(&env
->sregs
[INTSET
],
139 ~(v
& (env
->config
->inttype_mask
[INTTYPE_SOFTWARE
] |
140 env
->config
->inttype_mask
[INTTYPE_EDGE
])));
143 static uint32_t relocated_vector(CPUXtensaState
*env
, uint32_t vector
)
145 if (xtensa_option_enabled(env
->config
,
146 XTENSA_OPTION_RELOCATABLE_VECTOR
)) {
147 return vector
- env
->config
->vecbase
+ env
->sregs
[VECBASE
];
154 * Handle penging IRQ.
155 * For the high priority interrupt jump to the corresponding interrupt vector.
156 * For the level-1 interrupt convert it to either user, kernel or double
157 * exception with the 'level-1 interrupt' exception cause.
159 static void handle_interrupt(CPUXtensaState
*env
)
161 int level
= env
->pending_irq_level
;
163 if (level
> xtensa_get_cintlevel(env
) &&
164 level
<= env
->config
->nlevel
&&
165 (env
->config
->level_mask
[level
] &
167 env
->sregs
[INTENABLE
])) {
168 CPUState
*cs
= CPU(xtensa_env_get_cpu(env
));
171 env
->sregs
[EPC1
+ level
- 1] = env
->pc
;
172 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
174 (env
->sregs
[PS
] & ~PS_INTLEVEL
) | level
| PS_EXCM
;
175 env
->pc
= relocated_vector(env
,
176 env
->config
->interrupt_vector
[level
]);
178 env
->sregs
[EXCCAUSE
] = LEVEL1_INTERRUPT_CAUSE
;
180 if (env
->sregs
[PS
] & PS_EXCM
) {
181 if (env
->config
->ndepc
) {
182 env
->sregs
[DEPC
] = env
->pc
;
184 env
->sregs
[EPC1
] = env
->pc
;
186 cs
->exception_index
= EXC_DOUBLE
;
188 env
->sregs
[EPC1
] = env
->pc
;
189 cs
->exception_index
=
190 (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
192 env
->sregs
[PS
] |= PS_EXCM
;
194 env
->exception_taken
= 1;
198 /* Called from cpu_handle_interrupt with BQL held */
199 void xtensa_cpu_do_interrupt(CPUState
*cs
)
201 XtensaCPU
*cpu
= XTENSA_CPU(cs
);
202 CPUXtensaState
*env
= &cpu
->env
;
204 if (cs
->exception_index
== EXC_IRQ
) {
205 qemu_log_mask(CPU_LOG_INT
,
206 "%s(EXC_IRQ) level = %d, cintlevel = %d, "
207 "pc = %08x, a0 = %08x, ps = %08x, "
208 "intset = %08x, intenable = %08x, "
210 __func__
, env
->pending_irq_level
,
211 xtensa_get_cintlevel(env
),
212 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
213 env
->sregs
[INTSET
], env
->sregs
[INTENABLE
],
215 handle_interrupt(env
);
218 switch (cs
->exception_index
) {
219 case EXC_WINDOW_OVERFLOW4
:
220 case EXC_WINDOW_UNDERFLOW4
:
221 case EXC_WINDOW_OVERFLOW8
:
222 case EXC_WINDOW_UNDERFLOW8
:
223 case EXC_WINDOW_OVERFLOW12
:
224 case EXC_WINDOW_UNDERFLOW12
:
229 qemu_log_mask(CPU_LOG_INT
, "%s(%d) "
230 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
231 __func__
, cs
->exception_index
,
232 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
234 if (env
->config
->exception_vector
[cs
->exception_index
]) {
237 vector
= env
->config
->exception_vector
[cs
->exception_index
];
238 env
->pc
= relocated_vector(env
, vector
);
239 env
->exception_taken
= 1;
241 qemu_log_mask(CPU_LOG_INT
,
242 "%s(pc = %08x) bad exception_index: %d\n",
243 __func__
, env
->pc
, cs
->exception_index
);
251 qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
252 __func__
, env
->pc
, cs
->exception_index
);
255 check_interrupts(env
);
258 void xtensa_cpu_do_interrupt(CPUState
*cs
)
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
);