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 void HELPER(intclear
)(CPUXtensaState
*env
, uint32_t v
)
137 atomic_and(&env
->sregs
[INTSET
],
138 ~(v
& (env
->config
->inttype_mask
[INTTYPE_SOFTWARE
] |
139 env
->config
->inttype_mask
[INTTYPE_EDGE
])));
142 static uint32_t relocated_vector(CPUXtensaState
*env
, uint32_t vector
)
144 if (xtensa_option_enabled(env
->config
,
145 XTENSA_OPTION_RELOCATABLE_VECTOR
)) {
146 return vector
- env
->config
->vecbase
+ env
->sregs
[VECBASE
];
153 * Handle penging IRQ.
154 * For the high priority interrupt jump to the corresponding interrupt vector.
155 * For the level-1 interrupt convert it to either user, kernel or double
156 * exception with the 'level-1 interrupt' exception cause.
158 static void handle_interrupt(CPUXtensaState
*env
)
160 int level
= env
->pending_irq_level
;
162 if (level
> xtensa_get_cintlevel(env
) &&
163 level
<= env
->config
->nlevel
&&
164 (env
->config
->level_mask
[level
] &
166 env
->sregs
[INTENABLE
])) {
167 CPUState
*cs
= env_cpu(env
);
170 env
->sregs
[EPC1
+ level
- 1] = env
->pc
;
171 env
->sregs
[EPS2
+ level
- 2] = env
->sregs
[PS
];
173 (env
->sregs
[PS
] & ~PS_INTLEVEL
) | level
| PS_EXCM
;
174 env
->pc
= relocated_vector(env
,
175 env
->config
->interrupt_vector
[level
]);
177 env
->sregs
[EXCCAUSE
] = LEVEL1_INTERRUPT_CAUSE
;
179 if (env
->sregs
[PS
] & PS_EXCM
) {
180 if (env
->config
->ndepc
) {
181 env
->sregs
[DEPC
] = env
->pc
;
183 env
->sregs
[EPC1
] = env
->pc
;
185 cs
->exception_index
= EXC_DOUBLE
;
187 env
->sregs
[EPC1
] = env
->pc
;
188 cs
->exception_index
=
189 (env
->sregs
[PS
] & PS_UM
) ? EXC_USER
: EXC_KERNEL
;
191 env
->sregs
[PS
] |= PS_EXCM
;
193 env
->exception_taken
= 1;
197 /* Called from cpu_handle_interrupt with BQL held */
198 void xtensa_cpu_do_interrupt(CPUState
*cs
)
200 XtensaCPU
*cpu
= XTENSA_CPU(cs
);
201 CPUXtensaState
*env
= &cpu
->env
;
203 if (cs
->exception_index
== EXC_IRQ
) {
204 qemu_log_mask(CPU_LOG_INT
,
205 "%s(EXC_IRQ) level = %d, cintlevel = %d, "
206 "pc = %08x, a0 = %08x, ps = %08x, "
207 "intset = %08x, intenable = %08x, "
209 __func__
, env
->pending_irq_level
,
210 xtensa_get_cintlevel(env
),
211 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
212 env
->sregs
[INTSET
], env
->sregs
[INTENABLE
],
214 handle_interrupt(env
);
217 switch (cs
->exception_index
) {
218 case EXC_WINDOW_OVERFLOW4
:
219 case EXC_WINDOW_UNDERFLOW4
:
220 case EXC_WINDOW_OVERFLOW8
:
221 case EXC_WINDOW_UNDERFLOW8
:
222 case EXC_WINDOW_OVERFLOW12
:
223 case EXC_WINDOW_UNDERFLOW12
:
228 qemu_log_mask(CPU_LOG_INT
, "%s(%d) "
229 "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n",
230 __func__
, cs
->exception_index
,
231 env
->pc
, env
->regs
[0], env
->sregs
[PS
],
233 if (env
->config
->exception_vector
[cs
->exception_index
]) {
236 vector
= env
->config
->exception_vector
[cs
->exception_index
];
237 env
->pc
= relocated_vector(env
, vector
);
238 env
->exception_taken
= 1;
240 qemu_log_mask(CPU_LOG_INT
,
241 "%s(pc = %08x) bad exception_index: %d\n",
242 __func__
, env
->pc
, cs
->exception_index
);
250 qemu_log("%s(pc = %08x) unknown exception_index: %d\n",
251 __func__
, env
->pc
, cs
->exception_index
);
254 check_interrupts(env
);
257 void xtensa_cpu_do_interrupt(CPUState
*cs
)
262 bool xtensa_cpu_exec_interrupt(CPUState
*cs
, int interrupt_request
)
264 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
265 cs
->exception_index
= EXC_IRQ
;
266 xtensa_cpu_do_interrupt(cs
);