]>
Commit | Line | Data |
---|---|---|
cd71c089 LV |
1 | /* |
2 | * qemu user cpu loop | |
3 | * | |
4 | * Copyright (c) 2003-2008 Fabrice Bellard | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
21 | #include "qemu.h" | |
3b249d26 | 22 | #include "user-internals.h" |
cd71c089 | 23 | #include "cpu_loop-common.h" |
2113aed6 | 24 | #include "signal-common.h" |
cd71c089 | 25 | |
d0a28415 LV |
26 | #define SPARC64_STACK_BIAS 2047 |
27 | ||
28 | //#define DEBUG_WIN | |
29 | ||
30 | /* WARNING: dealing with register windows _is_ complicated. More info | |
31 | can be found at http://www.sics.se/~psm/sparcstack.html */ | |
32 | static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) | |
33 | { | |
34 | index = (index + cwp * 16) % (16 * env->nwindows); | |
35 | /* wrap handling : if cwp is on the last window, then we use the | |
36 | registers 'after' the end */ | |
37 | if (index < 8 && env->cwp == env->nwindows - 1) | |
38 | index += 16 * env->nwindows; | |
39 | return index; | |
40 | } | |
41 | ||
42 | /* save the register window 'cwp1' */ | |
43 | static inline void save_window_offset(CPUSPARCState *env, int cwp1) | |
44 | { | |
45 | unsigned int i; | |
46 | abi_ulong sp_ptr; | |
47 | ||
48 | sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; | |
49 | #ifdef TARGET_SPARC64 | |
50 | if (sp_ptr & 3) | |
51 | sp_ptr += SPARC64_STACK_BIAS; | |
52 | #endif | |
53 | #if defined(DEBUG_WIN) | |
54 | printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", | |
55 | sp_ptr, cwp1); | |
56 | #endif | |
57 | for(i = 0; i < 16; i++) { | |
58 | /* FIXME - what to do if put_user() fails? */ | |
59 | put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); | |
60 | sp_ptr += sizeof(abi_ulong); | |
61 | } | |
62 | } | |
63 | ||
64 | static void save_window(CPUSPARCState *env) | |
65 | { | |
66 | #ifndef TARGET_SPARC64 | |
67 | unsigned int new_wim; | |
68 | new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & | |
69 | ((1LL << env->nwindows) - 1); | |
70 | save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); | |
71 | env->wim = new_wim; | |
72 | #else | |
d43624c4 GM |
73 | /* |
74 | * cansave is zero if the spill trap handler is triggered by `save` and | |
75 | * nonzero if triggered by a `flushw` | |
76 | */ | |
77 | save_window_offset(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); | |
d0a28415 LV |
78 | env->cansave++; |
79 | env->canrestore--; | |
80 | #endif | |
81 | } | |
82 | ||
83 | static void restore_window(CPUSPARCState *env) | |
84 | { | |
85 | #ifndef TARGET_SPARC64 | |
86 | unsigned int new_wim; | |
87 | #endif | |
88 | unsigned int i, cwp1; | |
89 | abi_ulong sp_ptr; | |
90 | ||
91 | #ifndef TARGET_SPARC64 | |
92 | new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & | |
93 | ((1LL << env->nwindows) - 1); | |
94 | #endif | |
95 | ||
96 | /* restore the invalid window */ | |
97 | cwp1 = cpu_cwp_inc(env, env->cwp + 1); | |
98 | sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; | |
99 | #ifdef TARGET_SPARC64 | |
100 | if (sp_ptr & 3) | |
101 | sp_ptr += SPARC64_STACK_BIAS; | |
102 | #endif | |
103 | #if defined(DEBUG_WIN) | |
104 | printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", | |
105 | sp_ptr, cwp1); | |
106 | #endif | |
107 | for(i = 0; i < 16; i++) { | |
108 | /* FIXME - what to do if get_user() fails? */ | |
109 | get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); | |
110 | sp_ptr += sizeof(abi_ulong); | |
111 | } | |
112 | #ifdef TARGET_SPARC64 | |
113 | env->canrestore++; | |
114 | if (env->cleanwin < env->nwindows - 1) | |
115 | env->cleanwin++; | |
116 | env->cansave--; | |
117 | #else | |
118 | env->wim = new_wim; | |
119 | #endif | |
120 | } | |
121 | ||
122 | static void flush_windows(CPUSPARCState *env) | |
123 | { | |
124 | int offset, cwp1; | |
125 | ||
126 | offset = 1; | |
127 | for(;;) { | |
128 | /* if restore would invoke restore_window(), then we can stop */ | |
129 | cwp1 = cpu_cwp_inc(env, env->cwp + offset); | |
130 | #ifndef TARGET_SPARC64 | |
131 | if (env->wim & (1 << cwp1)) | |
132 | break; | |
133 | #else | |
134 | if (env->canrestore == 0) | |
135 | break; | |
136 | env->cansave++; | |
137 | env->canrestore--; | |
138 | #endif | |
139 | save_window_offset(env, cwp1); | |
140 | offset++; | |
141 | } | |
142 | cwp1 = cpu_cwp_inc(env, env->cwp + 1); | |
143 | #ifndef TARGET_SPARC64 | |
144 | /* set wim so that restore will reload the registers */ | |
145 | env->wim = 1 << cwp1; | |
146 | #endif | |
147 | #if defined(DEBUG_WIN) | |
148 | printf("flush_windows: nb=%d\n", offset - 1); | |
149 | #endif | |
150 | } | |
151 | ||
6abc58eb RH |
152 | static void next_instruction(CPUSPARCState *env) |
153 | { | |
154 | env->pc = env->npc; | |
155 | env->npc = env->npc + 4; | |
156 | } | |
157 | ||
158 | static uint32_t do_getcc(CPUSPARCState *env) | |
159 | { | |
160 | #ifdef TARGET_SPARC64 | |
161 | return cpu_get_ccr(env) & 0xf; | |
162 | #else | |
163 | return extract32(cpu_get_psr(env), 20, 4); | |
164 | #endif | |
165 | } | |
166 | ||
167 | static void do_setcc(CPUSPARCState *env, uint32_t icc) | |
168 | { | |
169 | #ifdef TARGET_SPARC64 | |
170 | cpu_put_ccr(env, (cpu_get_ccr(env) & 0xf0) | (icc & 0xf)); | |
171 | #else | |
172 | cpu_put_psr(env, deposit32(cpu_get_psr(env), 20, 4, icc)); | |
173 | #endif | |
174 | } | |
175 | ||
176 | static uint32_t do_getpsr(CPUSPARCState *env) | |
177 | { | |
178 | #ifdef TARGET_SPARC64 | |
179 | const uint64_t TSTATE_CWP = 0x1f; | |
180 | const uint64_t TSTATE_ICC = 0xfull << 32; | |
181 | const uint64_t TSTATE_XCC = 0xfull << 36; | |
182 | const uint32_t PSR_S = 0x00000080u; | |
183 | const uint32_t PSR_V8PLUS = 0xff000000u; | |
184 | uint64_t tstate = sparc64_tstate(env); | |
185 | ||
186 | /* See <asm/psrcompat.h>, tstate_to_psr. */ | |
187 | return ((tstate & TSTATE_CWP) | | |
188 | PSR_S | | |
189 | ((tstate & TSTATE_ICC) >> 12) | | |
190 | ((tstate & TSTATE_XCC) >> 20) | | |
191 | PSR_V8PLUS); | |
192 | #else | |
193 | return (cpu_get_psr(env) & (PSR_ICC | PSR_CWP)) | PSR_S; | |
194 | #endif | |
195 | } | |
196 | ||
9cee640a | 197 | /* Avoid ifdefs below for the abi32 and abi64 paths. */ |
3116f020 RH |
198 | #ifdef TARGET_ABI32 |
199 | #define TARGET_TT_SYSCALL (TT_TRAP + 0x10) /* t_linux */ | |
200 | #else | |
201 | #define TARGET_TT_SYSCALL (TT_TRAP + 0x6d) /* tl0_linux64 */ | |
202 | #endif | |
203 | ||
6f772241 RH |
204 | /* Avoid ifdefs below for the v9 and pre-v9 hw traps. */ |
205 | #ifdef TARGET_SPARC64 | |
206 | #define TARGET_TT_SPILL TT_SPILL | |
207 | #define TARGET_TT_FILL TT_FILL | |
208 | #else | |
209 | #define TARGET_TT_SPILL TT_WIN_OVF | |
210 | #define TARGET_TT_FILL TT_WIN_UNF | |
211 | #endif | |
212 | ||
d0a28415 LV |
213 | void cpu_loop (CPUSPARCState *env) |
214 | { | |
5a59fbce | 215 | CPUState *cs = env_cpu(env); |
d0a28415 LV |
216 | int trapnr; |
217 | abi_long ret; | |
d0a28415 LV |
218 | |
219 | while (1) { | |
220 | cpu_exec_start(cs); | |
221 | trapnr = cpu_exec(cs); | |
222 | cpu_exec_end(cs); | |
223 | process_queued_cpu_work(cs); | |
224 | ||
d0a28415 | 225 | switch (trapnr) { |
3116f020 | 226 | case TARGET_TT_SYSCALL: |
d0a28415 LV |
227 | ret = do_syscall (env, env->gregs[1], |
228 | env->regwptr[0], env->regwptr[1], | |
229 | env->regwptr[2], env->regwptr[3], | |
230 | env->regwptr[4], env->regwptr[5], | |
231 | 0, 0); | |
57a0c938 | 232 | if (ret == -QEMU_ERESTARTSYS || ret == -QEMU_ESIGRETURN) { |
d0a28415 LV |
233 | break; |
234 | } | |
235 | if ((abi_ulong)ret >= (abi_ulong)(-515)) { | |
2a1905c7 | 236 | set_syscall_C(env, 1); |
d0a28415 LV |
237 | ret = -ret; |
238 | } else { | |
2a1905c7 | 239 | set_syscall_C(env, 0); |
d0a28415 LV |
240 | } |
241 | env->regwptr[0] = ret; | |
242 | /* next instruction */ | |
243 | env->pc = env->npc; | |
244 | env->npc = env->npc + 4; | |
245 | break; | |
88cdb603 | 246 | |
52d104a5 RH |
247 | case TT_TRAP + 0x01: /* breakpoint */ |
248 | case EXCP_DEBUG: | |
249 | force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); | |
250 | break; | |
251 | ||
0908007f RH |
252 | case TT_TRAP + 0x02: /* div0 */ |
253 | case TT_DIV_ZERO: | |
254 | force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); | |
255 | break; | |
256 | ||
88cdb603 | 257 | case TT_TRAP + 0x03: /* flush windows */ |
d0a28415 | 258 | flush_windows(env); |
6abc58eb RH |
259 | next_instruction(env); |
260 | break; | |
261 | ||
262 | case TT_TRAP + 0x20: /* getcc */ | |
263 | env->gregs[1] = do_getcc(env); | |
264 | next_instruction(env); | |
265 | break; | |
266 | case TT_TRAP + 0x21: /* setcc */ | |
267 | do_setcc(env, env->gregs[1]); | |
268 | next_instruction(env); | |
269 | break; | |
270 | case TT_TRAP + 0x22: /* getpsr */ | |
271 | env->gregs[1] = do_getpsr(env); | |
272 | next_instruction(env); | |
d0a28415 | 273 | break; |
88cdb603 | 274 | |
6f772241 | 275 | #ifdef TARGET_SPARC64 |
d6b03637 | 276 | case TT_TRAP + 0x6e: |
d0a28415 LV |
277 | flush_windows(env); |
278 | sparc64_get_context(env); | |
279 | break; | |
d6b03637 | 280 | case TT_TRAP + 0x6f: |
d0a28415 LV |
281 | flush_windows(env); |
282 | sparc64_set_context(env); | |
283 | break; | |
284 | #endif | |
d6b03637 RH |
285 | |
286 | case TARGET_TT_SPILL: /* window overflow */ | |
287 | save_window(env); | |
288 | break; | |
289 | case TARGET_TT_FILL: /* window underflow */ | |
290 | restore_window(env); | |
291 | break; | |
292 | ||
4ea3af39 RH |
293 | case TT_FP_EXCP: |
294 | { | |
295 | int code = TARGET_FPE_FLTUNK; | |
1ccd6e13 | 296 | target_ulong fsr = cpu_get_fsr(env); |
4ea3af39 RH |
297 | |
298 | if ((fsr & FSR_FTT_MASK) == FSR_FTT_IEEE_EXCP) { | |
299 | if (fsr & FSR_NVC) { | |
300 | code = TARGET_FPE_FLTINV; | |
301 | } else if (fsr & FSR_OFC) { | |
302 | code = TARGET_FPE_FLTOVF; | |
303 | } else if (fsr & FSR_UFC) { | |
304 | code = TARGET_FPE_FLTUND; | |
305 | } else if (fsr & FSR_DZC) { | |
306 | code = TARGET_FPE_FLTDIV; | |
307 | } else if (fsr & FSR_NXC) { | |
308 | code = TARGET_FPE_FLTRES; | |
309 | } | |
310 | } | |
311 | force_sig_fault(TARGET_SIGFPE, code, env->pc); | |
312 | } | |
313 | break; | |
314 | ||
d0a28415 LV |
315 | case EXCP_INTERRUPT: |
316 | /* just indicate that signals should be handled asap */ | |
317 | break; | |
318 | case TT_ILL_INSN: | |
ac80d8b1 | 319 | force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); |
d0a28415 | 320 | break; |
97ff1478 RH |
321 | case TT_PRIV_INSN: |
322 | force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); | |
323 | break; | |
e64c6d42 RH |
324 | case TT_TOVF: |
325 | force_sig_fault(TARGET_SIGEMT, TARGET_EMT_TAGOVF, env->pc); | |
326 | break; | |
235f33b8 RH |
327 | #ifdef TARGET_SPARC64 |
328 | case TT_PRIV_ACT: | |
329 | /* Note do_privact defers to do_privop. */ | |
330 | force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); | |
331 | break; | |
c47d7c87 RH |
332 | #else |
333 | case TT_NCP_INSN: | |
334 | force_sig_fault(TARGET_SIGILL, TARGET_ILL_COPROC, env->pc); | |
335 | break; | |
81f04cd3 RH |
336 | case TT_UNIMP_FLUSH: |
337 | next_instruction(env); | |
338 | break; | |
235f33b8 | 339 | #endif |
d0a28415 LV |
340 | case EXCP_ATOMIC: |
341 | cpu_exec_step_atomic(cs); | |
342 | break; | |
343 | default: | |
21a474c4 RH |
344 | /* |
345 | * Most software trap numbers vector to BAD_TRAP. | |
346 | * Handle anything not explicitly matched above. | |
347 | */ | |
348 | if (trapnr >= TT_TRAP && trapnr <= TT_TRAP + 0x7f) { | |
349 | force_sig_fault(TARGET_SIGILL, ILL_ILLTRP, env->pc); | |
350 | break; | |
351 | } | |
84ca4fa9 | 352 | fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); |
90c84c56 | 353 | cpu_dump_state(cs, stderr, 0); |
d0a28415 LV |
354 | exit(EXIT_FAILURE); |
355 | } | |
356 | process_pending_signals (env); | |
357 | } | |
358 | } | |
359 | ||
cd71c089 LV |
360 | void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) |
361 | { | |
d0a28415 LV |
362 | int i; |
363 | env->pc = regs->pc; | |
364 | env->npc = regs->npc; | |
365 | env->y = regs->y; | |
366 | for(i = 0; i < 8; i++) | |
367 | env->gregs[i] = regs->u_regs[i]; | |
368 | for(i = 0; i < 8; i++) | |
369 | env->regwptr[i] = regs->u_regs[i + 8]; | |
cd71c089 | 370 | } |