]>
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" |
d9673512 | 23 | #include "elf.h" |
cd71c089 LV |
24 | #include "cpu_loop-common.h" |
25 | ||
d9673512 LV |
26 | #define get_user_code_u32(x, gaddr, env) \ |
27 | ({ abi_long __r = get_user_u32((x), (gaddr)); \ | |
28 | if (!__r && bswap_code(arm_sctlr_b(env))) { \ | |
29 | (x) = bswap32(x); \ | |
30 | } \ | |
31 | __r; \ | |
32 | }) | |
33 | ||
34 | #define get_user_code_u16(x, gaddr, env) \ | |
35 | ({ abi_long __r = get_user_u16((x), (gaddr)); \ | |
36 | if (!__r && bswap_code(arm_sctlr_b(env))) { \ | |
37 | (x) = bswap16(x); \ | |
38 | } \ | |
39 | __r; \ | |
40 | }) | |
41 | ||
42 | #define get_user_data_u32(x, gaddr, env) \ | |
43 | ({ abi_long __r = get_user_u32((x), (gaddr)); \ | |
44 | if (!__r && arm_cpu_bswap_data(env)) { \ | |
45 | (x) = bswap32(x); \ | |
46 | } \ | |
47 | __r; \ | |
48 | }) | |
49 | ||
50 | #define get_user_data_u16(x, gaddr, env) \ | |
51 | ({ abi_long __r = get_user_u16((x), (gaddr)); \ | |
52 | if (!__r && arm_cpu_bswap_data(env)) { \ | |
53 | (x) = bswap16(x); \ | |
54 | } \ | |
55 | __r; \ | |
56 | }) | |
57 | ||
58 | #define put_user_data_u32(x, gaddr, env) \ | |
59 | ({ typeof(x) __x = (x); \ | |
60 | if (arm_cpu_bswap_data(env)) { \ | |
61 | __x = bswap32(__x); \ | |
62 | } \ | |
63 | put_user_u32(__x, (gaddr)); \ | |
64 | }) | |
65 | ||
66 | #define put_user_data_u16(x, gaddr, env) \ | |
67 | ({ typeof(x) __x = (x); \ | |
68 | if (arm_cpu_bswap_data(env)) { \ | |
69 | __x = bswap16(__x); \ | |
70 | } \ | |
71 | put_user_u16(__x, (gaddr)); \ | |
72 | }) | |
73 | ||
74 | /* Commpage handling -- there is no commpage for AArch64 */ | |
75 | ||
76 | /* | |
77 | * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt | |
78 | * Input: | |
79 | * r0 = pointer to oldval | |
80 | * r1 = pointer to newval | |
81 | * r2 = pointer to target value | |
82 | * | |
83 | * Output: | |
84 | * r0 = 0 if *ptr was changed, non-0 if no exchange happened | |
85 | * C set if *ptr was changed, clear if no exchange happened | |
86 | * | |
87 | * Note segv's in kernel helpers are a bit tricky, we can set the | |
88 | * data address sensibly but the PC address is just the entry point. | |
89 | */ | |
90 | static void arm_kernel_cmpxchg64_helper(CPUARMState *env) | |
91 | { | |
92 | uint64_t oldval, newval, val; | |
93 | uint32_t addr, cpsr; | |
94 | target_siginfo_t info; | |
95 | ||
96 | /* Based on the 32 bit code in do_kernel_trap */ | |
97 | ||
98 | /* XXX: This only works between threads, not between processes. | |
99 | It's probably possible to implement this with native host | |
100 | operations. However things like ldrex/strex are much harder so | |
101 | there's not much point trying. */ | |
102 | start_exclusive(); | |
103 | cpsr = cpsr_read(env); | |
104 | addr = env->regs[2]; | |
105 | ||
106 | if (get_user_u64(oldval, env->regs[0])) { | |
107 | env->exception.vaddress = env->regs[0]; | |
108 | goto segv; | |
109 | }; | |
110 | ||
111 | if (get_user_u64(newval, env->regs[1])) { | |
112 | env->exception.vaddress = env->regs[1]; | |
113 | goto segv; | |
114 | }; | |
115 | ||
116 | if (get_user_u64(val, addr)) { | |
117 | env->exception.vaddress = addr; | |
118 | goto segv; | |
119 | } | |
120 | ||
121 | if (val == oldval) { | |
122 | val = newval; | |
123 | ||
124 | if (put_user_u64(val, addr)) { | |
125 | env->exception.vaddress = addr; | |
126 | goto segv; | |
127 | }; | |
128 | ||
129 | env->regs[0] = 0; | |
130 | cpsr |= CPSR_C; | |
131 | } else { | |
132 | env->regs[0] = -1; | |
133 | cpsr &= ~CPSR_C; | |
134 | } | |
135 | cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); | |
136 | end_exclusive(); | |
137 | return; | |
138 | ||
139 | segv: | |
140 | end_exclusive(); | |
141 | /* We get the PC of the entry address - which is as good as anything, | |
142 | on a real kernel what you get depends on which mode it uses. */ | |
143 | info.si_signo = TARGET_SIGSEGV; | |
144 | info.si_errno = 0; | |
145 | /* XXX: check env->error_code */ | |
146 | info.si_code = TARGET_SEGV_MAPERR; | |
147 | info._sifields._sigfault._addr = env->exception.vaddress; | |
148 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
149 | } | |
150 | ||
151 | /* Handle a jump to the kernel code page. */ | |
152 | static int | |
153 | do_kernel_trap(CPUARMState *env) | |
154 | { | |
155 | uint32_t addr; | |
156 | uint32_t cpsr; | |
157 | uint32_t val; | |
158 | ||
159 | switch (env->regs[15]) { | |
160 | case 0xffff0fa0: /* __kernel_memory_barrier */ | |
161 | /* ??? No-op. Will need to do better for SMP. */ | |
162 | break; | |
163 | case 0xffff0fc0: /* __kernel_cmpxchg */ | |
164 | /* XXX: This only works between threads, not between processes. | |
165 | It's probably possible to implement this with native host | |
166 | operations. However things like ldrex/strex are much harder so | |
167 | there's not much point trying. */ | |
168 | start_exclusive(); | |
169 | cpsr = cpsr_read(env); | |
170 | addr = env->regs[2]; | |
171 | /* FIXME: This should SEGV if the access fails. */ | |
172 | if (get_user_u32(val, addr)) | |
173 | val = ~env->regs[0]; | |
174 | if (val == env->regs[0]) { | |
175 | val = env->regs[1]; | |
176 | /* FIXME: Check for segfaults. */ | |
177 | put_user_u32(val, addr); | |
178 | env->regs[0] = 0; | |
179 | cpsr |= CPSR_C; | |
180 | } else { | |
181 | env->regs[0] = -1; | |
182 | cpsr &= ~CPSR_C; | |
183 | } | |
184 | cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); | |
185 | end_exclusive(); | |
186 | break; | |
187 | case 0xffff0fe0: /* __kernel_get_tls */ | |
188 | env->regs[0] = cpu_get_tls(env); | |
189 | break; | |
190 | case 0xffff0f60: /* __kernel_cmpxchg64 */ | |
191 | arm_kernel_cmpxchg64_helper(env); | |
192 | break; | |
193 | ||
194 | default: | |
195 | return 1; | |
196 | } | |
197 | /* Jump back to the caller. */ | |
198 | addr = env->regs[14]; | |
199 | if (addr & 1) { | |
200 | env->thumb = 1; | |
201 | addr &= ~1; | |
202 | } | |
203 | env->regs[15] = addr; | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | void cpu_loop(CPUARMState *env) | |
209 | { | |
2fc0cc0e | 210 | CPUState *cs = env_cpu(env); |
d9673512 LV |
211 | int trapnr; |
212 | unsigned int n, insn; | |
213 | target_siginfo_t info; | |
214 | uint32_t addr; | |
215 | abi_ulong ret; | |
216 | ||
217 | for(;;) { | |
218 | cpu_exec_start(cs); | |
219 | trapnr = cpu_exec(cs); | |
220 | cpu_exec_end(cs); | |
221 | process_queued_cpu_work(cs); | |
222 | ||
223 | switch(trapnr) { | |
224 | case EXCP_UDEF: | |
225 | case EXCP_NOCP: | |
226 | case EXCP_INVSTATE: | |
227 | { | |
228 | TaskState *ts = cs->opaque; | |
229 | uint32_t opcode; | |
230 | int rc; | |
231 | ||
232 | /* we handle the FPU emulation here, as Linux */ | |
233 | /* we get the opcode */ | |
234 | /* FIXME - what to do if get_user() fails? */ | |
235 | get_user_code_u32(opcode, env->regs[15], env); | |
236 | ||
237 | rc = EmulateAll(opcode, &ts->fpa, env); | |
238 | if (rc == 0) { /* illegal instruction */ | |
239 | info.si_signo = TARGET_SIGILL; | |
240 | info.si_errno = 0; | |
241 | info.si_code = TARGET_ILL_ILLOPN; | |
242 | info._sifields._sigfault._addr = env->regs[15]; | |
243 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
244 | } else if (rc < 0) { /* FP exception */ | |
245 | int arm_fpe=0; | |
246 | ||
247 | /* translate softfloat flags to FPSR flags */ | |
248 | if (-rc & float_flag_invalid) | |
249 | arm_fpe |= BIT_IOC; | |
250 | if (-rc & float_flag_divbyzero) | |
251 | arm_fpe |= BIT_DZC; | |
252 | if (-rc & float_flag_overflow) | |
253 | arm_fpe |= BIT_OFC; | |
254 | if (-rc & float_flag_underflow) | |
255 | arm_fpe |= BIT_UFC; | |
256 | if (-rc & float_flag_inexact) | |
257 | arm_fpe |= BIT_IXC; | |
258 | ||
259 | FPSR fpsr = ts->fpa.fpsr; | |
260 | //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); | |
261 | ||
262 | if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ | |
263 | info.si_signo = TARGET_SIGFPE; | |
264 | info.si_errno = 0; | |
265 | ||
266 | /* ordered by priority, least first */ | |
267 | if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES; | |
268 | if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND; | |
269 | if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF; | |
270 | if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV; | |
271 | if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; | |
272 | ||
273 | info._sifields._sigfault._addr = env->regs[15]; | |
274 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
275 | } else { | |
276 | env->regs[15] += 4; | |
277 | } | |
278 | ||
279 | /* accumulate unenabled exceptions */ | |
280 | if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC)) | |
281 | fpsr |= BIT_IXC; | |
282 | if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC)) | |
283 | fpsr |= BIT_UFC; | |
284 | if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC)) | |
285 | fpsr |= BIT_OFC; | |
286 | if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC)) | |
287 | fpsr |= BIT_DZC; | |
288 | if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC)) | |
289 | fpsr |= BIT_IOC; | |
290 | ts->fpa.fpsr=fpsr; | |
291 | } else { /* everything OK */ | |
292 | /* increment PC */ | |
293 | env->regs[15] += 4; | |
294 | } | |
295 | } | |
296 | break; | |
297 | case EXCP_SWI: | |
298 | case EXCP_BKPT: | |
299 | { | |
300 | env->eabi = 1; | |
301 | /* system call */ | |
302 | if (trapnr == EXCP_BKPT) { | |
303 | if (env->thumb) { | |
304 | /* FIXME - what to do if get_user() fails? */ | |
305 | get_user_code_u16(insn, env->regs[15], env); | |
306 | n = insn & 0xff; | |
307 | env->regs[15] += 2; | |
308 | } else { | |
309 | /* FIXME - what to do if get_user() fails? */ | |
310 | get_user_code_u32(insn, env->regs[15], env); | |
311 | n = (insn & 0xf) | ((insn >> 4) & 0xff0); | |
312 | env->regs[15] += 4; | |
313 | } | |
314 | } else { | |
315 | if (env->thumb) { | |
316 | /* FIXME - what to do if get_user() fails? */ | |
317 | get_user_code_u16(insn, env->regs[15] - 2, env); | |
318 | n = insn & 0xff; | |
319 | } else { | |
320 | /* FIXME - what to do if get_user() fails? */ | |
321 | get_user_code_u32(insn, env->regs[15] - 4, env); | |
322 | n = insn & 0xffffff; | |
323 | } | |
324 | } | |
325 | ||
326 | if (n == ARM_NR_cacheflush) { | |
327 | /* nop */ | |
328 | } else if (n == ARM_NR_semihosting | |
329 | || n == ARM_NR_thumb_semihosting) { | |
330 | env->regs[0] = do_arm_semihosting (env); | |
331 | } else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { | |
332 | /* linux syscall */ | |
333 | if (env->thumb || n == 0) { | |
334 | n = env->regs[7]; | |
335 | } else { | |
336 | n -= ARM_SYSCALL_BASE; | |
337 | env->eabi = 0; | |
338 | } | |
339 | if ( n > ARM_NR_BASE) { | |
340 | switch (n) { | |
341 | case ARM_NR_cacheflush: | |
342 | /* nop */ | |
343 | break; | |
344 | case ARM_NR_set_tls: | |
345 | cpu_set_tls(env, env->regs[0]); | |
346 | env->regs[0] = 0; | |
347 | break; | |
348 | case ARM_NR_breakpoint: | |
349 | env->regs[15] -= env->thumb ? 2 : 4; | |
350 | goto excp_debug; | |
62aaa514 CL |
351 | case ARM_NR_get_tls: |
352 | env->regs[0] = cpu_get_tls(env); | |
353 | break; | |
d9673512 LV |
354 | default: |
355 | gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", | |
356 | n); | |
357 | env->regs[0] = -TARGET_ENOSYS; | |
358 | break; | |
359 | } | |
360 | } else { | |
361 | ret = do_syscall(env, | |
362 | n, | |
363 | env->regs[0], | |
364 | env->regs[1], | |
365 | env->regs[2], | |
366 | env->regs[3], | |
367 | env->regs[4], | |
368 | env->regs[5], | |
369 | 0, 0); | |
370 | if (ret == -TARGET_ERESTARTSYS) { | |
371 | env->regs[15] -= env->thumb ? 2 : 4; | |
372 | } else if (ret != -TARGET_QEMU_ESIGRETURN) { | |
373 | env->regs[0] = ret; | |
374 | } | |
375 | } | |
376 | } else { | |
377 | goto error; | |
378 | } | |
379 | } | |
380 | break; | |
381 | case EXCP_SEMIHOST: | |
382 | env->regs[0] = do_arm_semihosting(env); | |
383 | break; | |
384 | case EXCP_INTERRUPT: | |
385 | /* just indicate that signals should be handled asap */ | |
386 | break; | |
387 | case EXCP_PREFETCH_ABORT: | |
388 | case EXCP_DATA_ABORT: | |
389 | addr = env->exception.vaddress; | |
390 | { | |
391 | info.si_signo = TARGET_SIGSEGV; | |
392 | info.si_errno = 0; | |
393 | /* XXX: check env->error_code */ | |
394 | info.si_code = TARGET_SEGV_MAPERR; | |
395 | info._sifields._sigfault._addr = addr; | |
396 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
397 | } | |
398 | break; | |
399 | case EXCP_DEBUG: | |
400 | excp_debug: | |
b10089a1 PM |
401 | info.si_signo = TARGET_SIGTRAP; |
402 | info.si_errno = 0; | |
403 | info.si_code = TARGET_TRAP_BRKPT; | |
404 | queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); | |
d9673512 LV |
405 | break; |
406 | case EXCP_KERNEL_TRAP: | |
407 | if (do_kernel_trap(env)) | |
408 | goto error; | |
409 | break; | |
410 | case EXCP_YIELD: | |
411 | /* nothing to do here for user-mode, just resume guest code */ | |
412 | break; | |
413 | case EXCP_ATOMIC: | |
414 | cpu_exec_step_atomic(cs); | |
415 | break; | |
416 | default: | |
417 | error: | |
418 | EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); | |
419 | abort(); | |
420 | } | |
421 | process_pending_signals(env); | |
422 | } | |
423 | } | |
424 | ||
cd71c089 LV |
425 | void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) |
426 | { | |
29a0af61 | 427 | CPUState *cpu = env_cpu(env); |
d9673512 LV |
428 | TaskState *ts = cpu->opaque; |
429 | struct image_info *info = ts->info; | |
430 | int i; | |
431 | ||
432 | cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, | |
433 | CPSRWriteByInstr); | |
434 | for(i = 0; i < 16; i++) { | |
435 | env->regs[i] = regs->uregs[i]; | |
436 | } | |
437 | #ifdef TARGET_WORDS_BIGENDIAN | |
438 | /* Enable BE8. */ | |
439 | if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 | |
440 | && (info->elf_flags & EF_ARM_BE8)) { | |
441 | env->uncached_cpsr |= CPSR_E; | |
442 | env->cp15.sctlr_el[1] |= SCTLR_E0E; | |
443 | } else { | |
444 | env->cp15.sctlr_el[1] |= SCTLR_B; | |
445 | } | |
446 | #endif | |
447 | ||
448 | ts->stack_base = info->start_stack; | |
449 | ts->heap_base = info->brk; | |
450 | /* This will be filled in on the first SYS_HEAPINFO call. */ | |
451 | ts->heap_limit = 0; | |
cd71c089 | 452 | } |