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-common.h" 22 #include "qemu.h" 23 #include "elf.h" 24 #include "cpu_loop-common.h" 25 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 { 210 CPUState *cs = env_cpu(env); 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 == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { 329 /* linux syscall */ 330 if (env->thumb || n == 0) { 331 n = env->regs[7]; 332 } else { 333 n -= ARM_SYSCALL_BASE; 334 env->eabi = 0; 335 } 336 if ( n > ARM_NR_BASE) { 337 switch (n) { 338 case ARM_NR_cacheflush: 339 /* nop */ 340 break; 341 case ARM_NR_set_tls: 342 cpu_set_tls(env, env->regs[0]); 343 env->regs[0] = 0; 344 break; 345 case ARM_NR_breakpoint: 346 env->regs[15] -= env->thumb ? 2 : 4; 347 goto excp_debug; 348 case ARM_NR_get_tls: 349 env->regs[0] = cpu_get_tls(env); 350 break; 351 default: 352 gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", 353 n); 354 env->regs[0] = -TARGET_ENOSYS; 355 break; 356 } 357 } else { 358 ret = do_syscall(env, 359 n, 360 env->regs[0], 361 env->regs[1], 362 env->regs[2], 363 env->regs[3], 364 env->regs[4], 365 env->regs[5], 366 0, 0); 367 if (ret == -TARGET_ERESTARTSYS) { 368 env->regs[15] -= env->thumb ? 2 : 4; 369 } else if (ret != -TARGET_QEMU_ESIGRETURN) { 370 env->regs[0] = ret; 371 } 372 } 373 } else { 374 goto error; 375 } 376 } 377 break; 378 case EXCP_SEMIHOST: 379 env->regs[0] = do_arm_semihosting(env); 380 break; 381 case EXCP_INTERRUPT: 382 /* just indicate that signals should be handled asap */ 383 break; 384 case EXCP_PREFETCH_ABORT: 385 case EXCP_DATA_ABORT: 386 addr = env->exception.vaddress; 387 { 388 info.si_signo = TARGET_SIGSEGV; 389 info.si_errno = 0; 390 /* XXX: check env->error_code */ 391 info.si_code = TARGET_SEGV_MAPERR; 392 info._sifields._sigfault._addr = addr; 393 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 394 } 395 break; 396 case EXCP_DEBUG: 397 excp_debug: 398 info.si_signo = TARGET_SIGTRAP; 399 info.si_errno = 0; 400 info.si_code = TARGET_TRAP_BRKPT; 401 queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); 402 break; 403 case EXCP_KERNEL_TRAP: 404 if (do_kernel_trap(env)) 405 goto error; 406 break; 407 case EXCP_YIELD: 408 /* nothing to do here for user-mode, just resume guest code */ 409 break; 410 case EXCP_ATOMIC: 411 cpu_exec_step_atomic(cs); 412 break; 413 default: 414 error: 415 EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); 416 abort(); 417 } 418 process_pending_signals(env); 419 } 420 } 421 422 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) 423 { 424 CPUState *cpu = env_cpu(env); 425 TaskState *ts = cpu->opaque; 426 struct image_info *info = ts->info; 427 int i; 428 429 cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, 430 CPSRWriteByInstr); 431 for(i = 0; i < 16; i++) { 432 env->regs[i] = regs->uregs[i]; 433 } 434 #ifdef TARGET_WORDS_BIGENDIAN 435 /* Enable BE8. */ 436 if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 437 && (info->elf_flags & EF_ARM_BE8)) { 438 env->uncached_cpsr |= CPSR_E; 439 env->cp15.sctlr_el[1] |= SCTLR_E0E; 440 } else { 441 env->cp15.sctlr_el[1] |= SCTLR_B; 442 } 443 #endif 444 445 ts->stack_base = info->start_stack; 446 ts->heap_base = info->brk; 447 /* This will be filled in on the first SYS_HEAPINFO call. */ 448 ts->heap_limit = 0; 449 } 450