]>
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" | |
a8d25326 | 21 | #include "qemu-common.h" |
cd71c089 | 22 | #include "qemu.h" |
3b249d26 | 23 | #include "user-internals.h" |
cd71c089 | 24 | #include "cpu_loop-common.h" |
2113aed6 | 25 | #include "signal-common.h" |
58908ef6 | 26 | #include "elf.h" |
502700d0 | 27 | #include "internal.h" |
81ddae7c | 28 | #include "fpu_helper.h" |
58908ef6 LV |
29 | |
30 | # ifdef TARGET_ABI_MIPSO32 | |
8d6d4c1b | 31 | # define MIPS_SYSCALL_NUMBER_UNUSED -1 |
8d6d4c1b | 32 | static const int8_t mips_syscall_args[] = { |
ac5d3c67 | 33 | #include "syscall-args-o32.c.inc" |
58908ef6 | 34 | }; |
58908ef6 LV |
35 | # endif /* O32 */ |
36 | ||
58908ef6 LV |
37 | /* Break codes */ |
38 | enum { | |
39 | BRK_OVERFLOW = 6, | |
40 | BRK_DIVZERO = 7 | |
41 | }; | |
42 | ||
43 | static int do_break(CPUMIPSState *env, target_siginfo_t *info, | |
44 | unsigned int code) | |
45 | { | |
46 | int ret = -1; | |
47 | ||
48 | switch (code) { | |
49 | case BRK_OVERFLOW: | |
50 | case BRK_DIVZERO: | |
51 | info->si_signo = TARGET_SIGFPE; | |
52 | info->si_errno = 0; | |
53 | info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; | |
54 | queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); | |
55 | ret = 0; | |
56 | break; | |
57 | default: | |
58 | info->si_signo = TARGET_SIGTRAP; | |
59 | info->si_errno = 0; | |
60 | queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); | |
61 | ret = 0; | |
62 | break; | |
63 | } | |
64 | ||
65 | return ret; | |
66 | } | |
67 | ||
68 | void cpu_loop(CPUMIPSState *env) | |
69 | { | |
5a7330b3 | 70 | CPUState *cs = env_cpu(env); |
58908ef6 LV |
71 | target_siginfo_t info; |
72 | int trapnr; | |
73 | abi_long ret; | |
74 | # ifdef TARGET_ABI_MIPSO32 | |
75 | unsigned int syscall_num; | |
76 | # endif | |
77 | ||
78 | for(;;) { | |
79 | cpu_exec_start(cs); | |
80 | trapnr = cpu_exec(cs); | |
81 | cpu_exec_end(cs); | |
82 | process_queued_cpu_work(cs); | |
83 | ||
84 | switch(trapnr) { | |
85 | case EXCP_SYSCALL: | |
86 | env->active_tc.PC += 4; | |
87 | # ifdef TARGET_ABI_MIPSO32 | |
88 | syscall_num = env->active_tc.gpr[2] - 4000; | |
89 | if (syscall_num >= sizeof(mips_syscall_args)) { | |
8d6d4c1b AM |
90 | /* syscall_num is larger that any defined for MIPS O32 */ |
91 | ret = -TARGET_ENOSYS; | |
92 | } else if (mips_syscall_args[syscall_num] == | |
93 | MIPS_SYSCALL_NUMBER_UNUSED) { | |
94 | /* syscall_num belongs to the range not defined for MIPS O32 */ | |
58908ef6 LV |
95 | ret = -TARGET_ENOSYS; |
96 | } else { | |
8d6d4c1b | 97 | /* syscall_num is valid */ |
58908ef6 LV |
98 | int nb_args; |
99 | abi_ulong sp_reg; | |
100 | abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; | |
101 | ||
102 | nb_args = mips_syscall_args[syscall_num]; | |
103 | sp_reg = env->active_tc.gpr[29]; | |
104 | switch (nb_args) { | |
105 | /* these arguments are taken from the stack */ | |
106 | case 8: | |
107 | if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { | |
108 | goto done_syscall; | |
109 | } | |
81966c18 | 110 | /* fall through */ |
58908ef6 LV |
111 | case 7: |
112 | if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { | |
113 | goto done_syscall; | |
114 | } | |
81966c18 | 115 | /* fall through */ |
58908ef6 LV |
116 | case 6: |
117 | if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { | |
118 | goto done_syscall; | |
119 | } | |
81966c18 | 120 | /* fall through */ |
58908ef6 LV |
121 | case 5: |
122 | if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { | |
123 | goto done_syscall; | |
124 | } | |
81966c18 | 125 | /* fall through */ |
58908ef6 LV |
126 | default: |
127 | break; | |
128 | } | |
129 | ret = do_syscall(env, env->active_tc.gpr[2], | |
130 | env->active_tc.gpr[4], | |
131 | env->active_tc.gpr[5], | |
132 | env->active_tc.gpr[6], | |
133 | env->active_tc.gpr[7], | |
134 | arg5, arg6, arg7, arg8); | |
135 | } | |
136 | done_syscall: | |
137 | # else | |
138 | ret = do_syscall(env, env->active_tc.gpr[2], | |
139 | env->active_tc.gpr[4], env->active_tc.gpr[5], | |
140 | env->active_tc.gpr[6], env->active_tc.gpr[7], | |
141 | env->active_tc.gpr[8], env->active_tc.gpr[9], | |
142 | env->active_tc.gpr[10], env->active_tc.gpr[11]); | |
143 | # endif /* O32 */ | |
af254a27 | 144 | if (ret == -QEMU_ERESTARTSYS) { |
58908ef6 LV |
145 | env->active_tc.PC -= 4; |
146 | break; | |
147 | } | |
57a0c938 | 148 | if (ret == -QEMU_ESIGRETURN) { |
58908ef6 LV |
149 | /* Returning from a successful sigreturn syscall. |
150 | Avoid clobbering register state. */ | |
151 | break; | |
152 | } | |
153 | if ((abi_ulong)ret >= (abi_ulong)-1133) { | |
154 | env->active_tc.gpr[7] = 1; /* error flag */ | |
155 | ret = -ret; | |
156 | } else { | |
157 | env->active_tc.gpr[7] = 0; /* error flag */ | |
158 | } | |
159 | env->active_tc.gpr[2] = ret; | |
160 | break; | |
58908ef6 LV |
161 | case EXCP_CpU: |
162 | case EXCP_RI: | |
163 | info.si_signo = TARGET_SIGILL; | |
164 | info.si_errno = 0; | |
165 | info.si_code = 0; | |
166 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
167 | break; | |
168 | case EXCP_INTERRUPT: | |
169 | /* just indicate that signals should be handled asap */ | |
170 | break; | |
171 | case EXCP_DEBUG: | |
b10089a1 PM |
172 | info.si_signo = TARGET_SIGTRAP; |
173 | info.si_errno = 0; | |
174 | info.si_code = TARGET_TRAP_BRKPT; | |
175 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
58908ef6 | 176 | break; |
58908ef6 LV |
177 | case EXCP_DSPDIS: |
178 | info.si_signo = TARGET_SIGILL; | |
179 | info.si_errno = 0; | |
180 | info.si_code = TARGET_ILL_ILLOPC; | |
181 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
182 | break; | |
64ce541c AM |
183 | case EXCP_FPE: |
184 | info.si_signo = TARGET_SIGFPE; | |
185 | info.si_errno = 0; | |
186 | info.si_code = TARGET_FPE_FLTUNK; | |
187 | if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { | |
188 | info.si_code = TARGET_FPE_FLTINV; | |
189 | } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) { | |
190 | info.si_code = TARGET_FPE_FLTDIV; | |
191 | } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) { | |
192 | info.si_code = TARGET_FPE_FLTOVF; | |
193 | } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) { | |
194 | info.si_code = TARGET_FPE_FLTUND; | |
195 | } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) { | |
196 | info.si_code = TARGET_FPE_FLTRES; | |
197 | } | |
198 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
199 | break; | |
58908ef6 LV |
200 | /* The code below was inspired by the MIPS Linux kernel trap |
201 | * handling code in arch/mips/kernel/traps.c. | |
202 | */ | |
203 | case EXCP_BREAK: | |
204 | { | |
205 | abi_ulong trap_instr; | |
206 | unsigned int code; | |
207 | ||
208 | if (env->hflags & MIPS_HFLAG_M16) { | |
209 | if (env->insn_flags & ASE_MICROMIPS) { | |
210 | /* microMIPS mode */ | |
211 | ret = get_user_u16(trap_instr, env->active_tc.PC); | |
212 | if (ret != 0) { | |
213 | goto error; | |
214 | } | |
215 | ||
216 | if ((trap_instr >> 10) == 0x11) { | |
217 | /* 16-bit instruction */ | |
218 | code = trap_instr & 0xf; | |
219 | } else { | |
220 | /* 32-bit instruction */ | |
221 | abi_ulong instr_lo; | |
222 | ||
223 | ret = get_user_u16(instr_lo, | |
224 | env->active_tc.PC + 2); | |
225 | if (ret != 0) { | |
226 | goto error; | |
227 | } | |
228 | trap_instr = (trap_instr << 16) | instr_lo; | |
229 | code = ((trap_instr >> 6) & ((1 << 20) - 1)); | |
230 | /* Unfortunately, microMIPS also suffers from | |
231 | the old assembler bug... */ | |
232 | if (code >= (1 << 10)) { | |
233 | code >>= 10; | |
234 | } | |
235 | } | |
236 | } else { | |
237 | /* MIPS16e mode */ | |
238 | ret = get_user_u16(trap_instr, env->active_tc.PC); | |
239 | if (ret != 0) { | |
240 | goto error; | |
241 | } | |
242 | code = (trap_instr >> 6) & 0x3f; | |
243 | } | |
244 | } else { | |
245 | ret = get_user_u32(trap_instr, env->active_tc.PC); | |
246 | if (ret != 0) { | |
247 | goto error; | |
248 | } | |
249 | ||
250 | /* As described in the original Linux kernel code, the | |
251 | * below checks on 'code' are to work around an old | |
252 | * assembly bug. | |
253 | */ | |
254 | code = ((trap_instr >> 6) & ((1 << 20) - 1)); | |
255 | if (code >= (1 << 10)) { | |
256 | code >>= 10; | |
257 | } | |
258 | } | |
259 | ||
260 | if (do_break(env, &info, code) != 0) { | |
261 | goto error; | |
262 | } | |
263 | } | |
264 | break; | |
265 | case EXCP_TRAP: | |
266 | { | |
267 | abi_ulong trap_instr; | |
268 | unsigned int code = 0; | |
269 | ||
270 | if (env->hflags & MIPS_HFLAG_M16) { | |
271 | /* microMIPS mode */ | |
272 | abi_ulong instr[2]; | |
273 | ||
274 | ret = get_user_u16(instr[0], env->active_tc.PC) || | |
275 | get_user_u16(instr[1], env->active_tc.PC + 2); | |
276 | ||
277 | trap_instr = (instr[0] << 16) | instr[1]; | |
278 | } else { | |
279 | ret = get_user_u32(trap_instr, env->active_tc.PC); | |
280 | } | |
281 | ||
282 | if (ret != 0) { | |
283 | goto error; | |
284 | } | |
285 | ||
286 | /* The immediate versions don't provide a code. */ | |
287 | if (!(trap_instr & 0xFC000000)) { | |
288 | if (env->hflags & MIPS_HFLAG_M16) { | |
289 | /* microMIPS mode */ | |
290 | code = ((trap_instr >> 12) & ((1 << 4) - 1)); | |
291 | } else { | |
292 | code = ((trap_instr >> 6) & ((1 << 10) - 1)); | |
293 | } | |
294 | } | |
295 | ||
296 | if (do_break(env, &info, code) != 0) { | |
297 | goto error; | |
298 | } | |
299 | } | |
300 | break; | |
301 | case EXCP_ATOMIC: | |
302 | cpu_exec_step_atomic(cs); | |
303 | break; | |
304 | default: | |
305 | error: | |
306 | EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); | |
307 | abort(); | |
308 | } | |
309 | process_pending_signals(env); | |
310 | } | |
311 | } | |
cd71c089 LV |
312 | |
313 | void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) | |
314 | { | |
29a0af61 | 315 | CPUState *cpu = env_cpu(env); |
58908ef6 LV |
316 | TaskState *ts = cpu->opaque; |
317 | struct image_info *info = ts->info; | |
318 | int i; | |
319 | ||
0c1bbedc SM |
320 | struct mode_req { |
321 | bool single; | |
322 | bool soft; | |
323 | bool fr1; | |
324 | bool frdefault; | |
325 | bool fre; | |
326 | }; | |
327 | ||
328 | static const struct mode_req fpu_reqs[] = { | |
329 | [MIPS_ABI_FP_ANY] = { true, true, true, true, true }, | |
330 | [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true }, | |
331 | [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false }, | |
332 | [MIPS_ABI_FP_SOFT] = { false, true, false, false, false }, | |
333 | [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false }, | |
334 | [MIPS_ABI_FP_XX] = { false, false, true, true, true }, | |
335 | [MIPS_ABI_FP_64] = { false, false, true, false, false }, | |
336 | [MIPS_ABI_FP_64A] = { false, false, true, false, true } | |
337 | }; | |
338 | ||
339 | /* | |
340 | * Mode requirements when .MIPS.abiflags is not present in the ELF. | |
341 | * Not present means that everything is acceptable except FR1. | |
342 | */ | |
343 | static struct mode_req none_req = { true, true, false, true, true }; | |
344 | ||
345 | struct mode_req prog_req; | |
346 | struct mode_req interp_req; | |
347 | ||
58908ef6 LV |
348 | for(i = 0; i < 32; i++) { |
349 | env->active_tc.gpr[i] = regs->regs[i]; | |
350 | } | |
351 | env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; | |
352 | if (regs->cp0_epc & 1) { | |
353 | env->hflags |= MIPS_HFLAG_M16; | |
354 | } | |
0c1bbedc SM |
355 | |
356 | #ifdef TARGET_ABI_MIPSO32 | |
357 | # define MAX_FP_ABI MIPS_ABI_FP_64A | |
358 | #else | |
359 | # define MAX_FP_ABI MIPS_ABI_FP_SOFT | |
360 | #endif | |
361 | if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN) | |
362 | || (info->interp_fp_abi > MAX_FP_ABI && | |
363 | info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) { | |
364 | fprintf(stderr, "qemu: Unexpected FPU mode\n"); | |
365 | exit(1); | |
366 | } | |
367 | ||
368 | prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req | |
369 | : fpu_reqs[info->fp_abi]; | |
370 | interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req | |
371 | : fpu_reqs[info->interp_fp_abi]; | |
372 | ||
373 | prog_req.single &= interp_req.single; | |
374 | prog_req.soft &= interp_req.soft; | |
375 | prog_req.fr1 &= interp_req.fr1; | |
376 | prog_req.frdefault &= interp_req.frdefault; | |
377 | prog_req.fre &= interp_req.fre; | |
378 | ||
7a47bae5 | 379 | bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS_R2 || |
2e211e0a | 380 | env->insn_flags & ISA_MIPS_R6; |
0c1bbedc SM |
381 | |
382 | if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) { | |
383 | env->CP0_Config5 |= (1 << CP0C5_FRE); | |
384 | if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { | |
385 | env->hflags |= MIPS_HFLAG_FRE; | |
386 | } | |
387 | } else if ((prog_req.fr1 && prog_req.frdefault) || | |
388 | (prog_req.single && !prog_req.frdefault)) { | |
389 | if ((env->active_fpu.fcr0 & (1 << FCR0_F64) | |
390 | && cpu_has_mips_r2_r6) || prog_req.fr1) { | |
391 | env->CP0_Status |= (1 << CP0St_FR); | |
392 | env->hflags |= MIPS_HFLAG_F64; | |
393 | } | |
394 | } else if (!prog_req.fre && !prog_req.frdefault && | |
395 | !prog_req.fr1 && !prog_req.single && !prog_req.soft) { | |
396 | fprintf(stderr, "qemu: Can't find a matching FPU mode\n"); | |
397 | exit(1); | |
398 | } | |
399 | ||
722ac96c AM |
400 | if (env->insn_flags & ISA_NANOMIPS32) { |
401 | return; | |
402 | } | |
58908ef6 LV |
403 | if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != |
404 | ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { | |
405 | if ((env->active_fpu.fcr31_rw_bitmask & | |
406 | (1 << FCR31_NAN2008)) == 0) { | |
407 | fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); | |
408 | exit(1); | |
409 | } | |
410 | if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { | |
411 | env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); | |
412 | } else { | |
413 | env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); | |
414 | } | |
415 | restore_snan_bit_mode(env); | |
416 | } | |
cd71c089 | 417 | } |