| 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" |
| 22 | #include "qemu/timer.h" |
| 23 | #include "user-internals.h" |
| 24 | #include "user/cpu_loop.h" |
| 25 | #include "signal-common.h" |
| 26 | #include "user-mmap.h" |
| 27 | |
| 28 | /***********************************************************/ |
| 29 | /* CPUX86 core interface */ |
| 30 | |
| 31 | uint64_t cpu_get_tsc(CPUX86State *env) |
| 32 | { |
| 33 | return cpu_get_host_ticks(); |
| 34 | } |
| 35 | |
| 36 | static void write_dt(void *ptr, unsigned long addr, unsigned long limit, |
| 37 | int flags) |
| 38 | { |
| 39 | unsigned int e1, e2; |
| 40 | uint32_t *p; |
| 41 | e1 = (addr << 16) | (limit & 0xffff); |
| 42 | e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); |
| 43 | e2 |= flags; |
| 44 | p = ptr; |
| 45 | p[0] = tswap32(e1); |
| 46 | p[1] = tswap32(e2); |
| 47 | } |
| 48 | |
| 49 | static uint64_t *idt_table; |
| 50 | |
| 51 | static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, |
| 52 | uint64_t addr, unsigned int sel) |
| 53 | { |
| 54 | uint32_t *p, e1, e2; |
| 55 | e1 = (addr & 0xffff) | (sel << 16); |
| 56 | e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); |
| 57 | p = ptr; |
| 58 | p[0] = tswap32(e1); |
| 59 | p[1] = tswap32(e2); |
| 60 | p[2] = tswap32(addr >> 32); |
| 61 | p[3] = 0; |
| 62 | } |
| 63 | |
| 64 | #ifdef TARGET_X86_64 |
| 65 | /* only dpl matters as we do only user space emulation */ |
| 66 | static void set_idt(int n, unsigned int dpl, bool is64) |
| 67 | { |
| 68 | set_gate64(idt_table + n * 2, 0, dpl, 0, 0); |
| 69 | } |
| 70 | #else |
| 71 | static void set_gate(void *ptr, unsigned int type, unsigned int dpl, |
| 72 | uint32_t addr, unsigned int sel) |
| 73 | { |
| 74 | uint32_t *p, e1, e2; |
| 75 | e1 = (addr & 0xffff) | (sel << 16); |
| 76 | e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); |
| 77 | p = ptr; |
| 78 | p[0] = tswap32(e1); |
| 79 | p[1] = tswap32(e2); |
| 80 | } |
| 81 | |
| 82 | /* only dpl matters as we do only user space emulation */ |
| 83 | static void set_idt(int n, unsigned int dpl, bool is64) |
| 84 | { |
| 85 | if (is64) { |
| 86 | set_gate64(idt_table + n * 2, 0, dpl, 0, 0); |
| 87 | } else { |
| 88 | set_gate(idt_table + n, 0, dpl, 0, 0); |
| 89 | } |
| 90 | } |
| 91 | #endif |
| 92 | |
| 93 | #ifdef TARGET_X86_64 |
| 94 | static bool write_ok_or_segv(CPUX86State *env, abi_ptr addr, size_t len) |
| 95 | { |
| 96 | /* |
| 97 | * For all the vsyscalls, NULL means "don't write anything" not |
| 98 | * "write it at address 0". |
| 99 | */ |
| 100 | if (addr == 0 || access_ok(env_cpu(env), VERIFY_WRITE, addr, len)) { |
| 101 | return true; |
| 102 | } |
| 103 | |
| 104 | env->error_code = PG_ERROR_W_MASK | PG_ERROR_U_MASK; |
| 105 | force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, addr); |
| 106 | return false; |
| 107 | } |
| 108 | |
| 109 | /* |
| 110 | * Since v3.1, the kernel traps and emulates the vsyscall page. |
| 111 | * Entry points other than the official generate SIGSEGV. |
| 112 | */ |
| 113 | static void emulate_vsyscall(CPUX86State *env) |
| 114 | { |
| 115 | int syscall; |
| 116 | abi_ulong ret; |
| 117 | uint64_t caller; |
| 118 | |
| 119 | /* |
| 120 | * Validate the entry point. We have already validated the page |
| 121 | * during translation to get here; now verify the offset. |
| 122 | */ |
| 123 | switch (env->eip & ~TARGET_PAGE_MASK) { |
| 124 | case 0x000: |
| 125 | syscall = TARGET_NR_gettimeofday; |
| 126 | break; |
| 127 | case 0x400: |
| 128 | syscall = TARGET_NR_time; |
| 129 | break; |
| 130 | case 0x800: |
| 131 | syscall = TARGET_NR_getcpu; |
| 132 | break; |
| 133 | default: |
| 134 | goto sigsegv; |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | * Validate the return address. |
| 139 | * Note that the kernel treats this the same as an invalid entry point. |
| 140 | */ |
| 141 | if (get_user_u64(caller, env->regs[R_ESP])) { |
| 142 | goto sigsegv; |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * Validate the pointer arguments. |
| 147 | */ |
| 148 | switch (syscall) { |
| 149 | case TARGET_NR_gettimeofday: |
| 150 | if (!write_ok_or_segv(env, env->regs[R_EDI], |
| 151 | sizeof(struct target_timeval)) || |
| 152 | !write_ok_or_segv(env, env->regs[R_ESI], |
| 153 | sizeof(struct target_timezone))) { |
| 154 | return; |
| 155 | } |
| 156 | break; |
| 157 | case TARGET_NR_time: |
| 158 | if (!write_ok_or_segv(env, env->regs[R_EDI], sizeof(abi_long))) { |
| 159 | return; |
| 160 | } |
| 161 | break; |
| 162 | case TARGET_NR_getcpu: |
| 163 | if (!write_ok_or_segv(env, env->regs[R_EDI], sizeof(uint32_t)) || |
| 164 | !write_ok_or_segv(env, env->regs[R_ESI], sizeof(uint32_t))) { |
| 165 | return; |
| 166 | } |
| 167 | break; |
| 168 | default: |
| 169 | g_assert_not_reached(); |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | * Perform the syscall. None of the vsyscalls should need restarting. |
| 174 | */ |
| 175 | get_task_state(env_cpu(env))->orig_ax = syscall; |
| 176 | ret = do_syscall(env, syscall, env->regs[R_EDI], env->regs[R_ESI], |
| 177 | env->regs[R_EDX], env->regs[10], env->regs[8], |
| 178 | env->regs[9], 0, 0); |
| 179 | g_assert(ret != -QEMU_ERESTARTSYS); |
| 180 | g_assert(ret != -QEMU_ESIGRETURN); |
| 181 | if (ret == -TARGET_EFAULT) { |
| 182 | goto sigsegv; |
| 183 | } |
| 184 | if (ret != -QEMU_ESETPC) { |
| 185 | env->regs[R_EAX] = ret; |
| 186 | } |
| 187 | |
| 188 | /* Emulate a ret instruction to leave the vsyscall page. */ |
| 189 | env->eip = caller; |
| 190 | env->regs[R_ESP] += 8; |
| 191 | return; |
| 192 | |
| 193 | sigsegv: |
| 194 | force_sig(TARGET_SIGSEGV); |
| 195 | } |
| 196 | #endif |
| 197 | |
| 198 | static bool maybe_handle_vm86_trap(CPUX86State *env, int trapnr) |
| 199 | { |
| 200 | #ifndef TARGET_X86_64 |
| 201 | if (env->eflags & VM_MASK) { |
| 202 | handle_vm86_trap(env, trapnr); |
| 203 | return true; |
| 204 | } |
| 205 | #endif |
| 206 | return false; |
| 207 | } |
| 208 | |
| 209 | void cpu_loop(CPUX86State *env) |
| 210 | { |
| 211 | CPUState *cs = env_cpu(env); |
| 212 | int trapnr; |
| 213 | abi_ulong ret; |
| 214 | |
| 215 | for(;;) { |
| 216 | cpu_exec_start(cs); |
| 217 | trapnr = cpu_exec(cs); |
| 218 | cpu_exec_end(cs); |
| 219 | qemu_process_cpu_events(cs); |
| 220 | |
| 221 | switch(trapnr) { |
| 222 | case 0x80: |
| 223 | #ifndef TARGET_X86_64 |
| 224 | case EXCP_SYSCALL: |
| 225 | #endif |
| 226 | /* linux syscall from int $0x80 */ |
| 227 | get_task_state(cs)->orig_ax = env->regs[R_EAX]; |
| 228 | ret = do_syscall(env, |
| 229 | env->regs[R_EAX], |
| 230 | env->regs[R_EBX], |
| 231 | env->regs[R_ECX], |
| 232 | env->regs[R_EDX], |
| 233 | env->regs[R_ESI], |
| 234 | env->regs[R_EDI], |
| 235 | env->regs[R_EBP], |
| 236 | 0, 0); |
| 237 | if (ret == -QEMU_ERESTARTSYS) { |
| 238 | env->eip -= 2; |
| 239 | } else if (ret != -QEMU_ESIGRETURN && ret != -QEMU_ESETPC) { |
| 240 | env->regs[R_EAX] = ret; |
| 241 | } |
| 242 | break; |
| 243 | #ifdef TARGET_X86_64 |
| 244 | case EXCP_SYSCALL: |
| 245 | /* linux syscall from syscall instruction. */ |
| 246 | get_task_state(cs)->orig_ax = env->regs[R_EAX]; |
| 247 | ret = do_syscall(env, |
| 248 | env->regs[R_EAX], |
| 249 | env->regs[R_EDI], |
| 250 | env->regs[R_ESI], |
| 251 | env->regs[R_EDX], |
| 252 | env->regs[10], |
| 253 | env->regs[8], |
| 254 | env->regs[9], |
| 255 | 0, 0); |
| 256 | if (ret == -QEMU_ERESTARTSYS) { |
| 257 | env->eip -= 2; |
| 258 | } else if (ret != -QEMU_ESIGRETURN && ret != -QEMU_ESETPC) { |
| 259 | env->regs[R_EAX] = ret; |
| 260 | } |
| 261 | break; |
| 262 | case EXCP_VSYSCALL: |
| 263 | emulate_vsyscall(env); |
| 264 | break; |
| 265 | #endif |
| 266 | case EXCP0B_NOSEG: |
| 267 | case EXCP0C_STACK: |
| 268 | force_sig(TARGET_SIGBUS); |
| 269 | break; |
| 270 | case EXCP0D_GPF: |
| 271 | /* XXX: potential problem if ABI32 */ |
| 272 | if (maybe_handle_vm86_trap(env, trapnr)) { |
| 273 | break; |
| 274 | } |
| 275 | force_sig(TARGET_SIGSEGV); |
| 276 | break; |
| 277 | case EXCP0E_PAGE: |
| 278 | force_sig_fault(TARGET_SIGSEGV, |
| 279 | (env->error_code & PG_ERROR_P_MASK ? |
| 280 | TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR), |
| 281 | env->cr[2]); |
| 282 | break; |
| 283 | case EXCP00_DIVZ: |
| 284 | if (maybe_handle_vm86_trap(env, trapnr)) { |
| 285 | break; |
| 286 | } |
| 287 | force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->eip); |
| 288 | break; |
| 289 | case EXCP01_DB: |
| 290 | if (maybe_handle_vm86_trap(env, trapnr)) { |
| 291 | break; |
| 292 | } |
| 293 | force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->eip); |
| 294 | break; |
| 295 | case EXCP03_INT3: |
| 296 | if (maybe_handle_vm86_trap(env, trapnr)) { |
| 297 | break; |
| 298 | } |
| 299 | force_sig(TARGET_SIGTRAP); |
| 300 | break; |
| 301 | case EXCP04_INTO: |
| 302 | case EXCP05_BOUND: |
| 303 | if (maybe_handle_vm86_trap(env, trapnr)) { |
| 304 | break; |
| 305 | } |
| 306 | force_sig(TARGET_SIGSEGV); |
| 307 | break; |
| 308 | case EXCP06_ILLOP: |
| 309 | force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->eip); |
| 310 | break; |
| 311 | case EXCP_INTERRUPT: |
| 312 | /* just indicate that signals should be handled asap */ |
| 313 | break; |
| 314 | case EXCP_DEBUG: |
| 315 | force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->eip); |
| 316 | break; |
| 317 | case EXCP_ATOMIC: |
| 318 | cpu_exec_step_atomic(cs); |
| 319 | break; |
| 320 | default: |
| 321 | EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", |
| 322 | trapnr); |
| 323 | abort(); |
| 324 | } |
| 325 | process_pending_signals(env); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | static void target_cpu_free(void *obj) |
| 330 | { |
| 331 | target_munmap(cpu_env(obj)->gdt.base, |
| 332 | sizeof(uint64_t) * TARGET_GDT_ENTRIES); |
| 333 | g_free(obj); |
| 334 | } |
| 335 | |
| 336 | void init_main_thread(CPUState *cpu, struct image_info *info) |
| 337 | { |
| 338 | CPUArchState *env = cpu_env(cpu); |
| 339 | bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; |
| 340 | |
| 341 | OBJECT(cpu)->free = target_cpu_free; |
| 342 | env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; |
| 343 | env->hflags |= HF_PE_MASK | HF_CPL_MASK; |
| 344 | if (env->features[FEAT_1_EDX] & CPUID_SSE) { |
| 345 | env->cr[4] |= CR4_OSFXSR_MASK; |
| 346 | env->hflags |= HF_OSFXSR_MASK; |
| 347 | } |
| 348 | |
| 349 | /* enable 64 bit mode if possible */ |
| 350 | if (is64) { |
| 351 | env->cr[4] |= CR4_PAE_MASK; |
| 352 | env->efer |= MSR_EFER_LMA | MSR_EFER_LME; |
| 353 | env->hflags |= HF_LMA_MASK; |
| 354 | } |
| 355 | #ifndef TARGET_ABI32 |
| 356 | else { |
| 357 | fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); |
| 358 | exit(EXIT_FAILURE); |
| 359 | } |
| 360 | #endif |
| 361 | |
| 362 | /* flags setup : we activate the IRQs by default as in user mode */ |
| 363 | env->eflags |= IF_MASK; |
| 364 | |
| 365 | /* |
| 366 | * Linux register setup. |
| 367 | * |
| 368 | * SVR4/i386 ABI (pages 3-31, 3-32) says that when the program |
| 369 | * starts %edx contains a pointer to a function which might be |
| 370 | * registered using `atexit'. This provides a mean for the |
| 371 | * dynamic linker to call DT_FINI functions for shared libraries |
| 372 | * that have been loaded before the code runs. |
| 373 | * A value of 0 tells we have no such handler. |
| 374 | * |
| 375 | * This applies to x86_64 as well as i386. |
| 376 | * |
| 377 | * That said, the kernel's ELF_PLAT_INIT simply zeros all of the general |
| 378 | * registers. Note that x86_cpu_reset_hold will set %edx to cpuid_version; |
| 379 | * clear all general registers defensively. |
| 380 | */ |
| 381 | memset(env->regs, 0, sizeof(env->regs)); |
| 382 | env->regs[R_ESP] = info->start_stack; |
| 383 | env->eip = info->entry; |
| 384 | |
| 385 | /* linux interrupt setup */ |
| 386 | #ifndef TARGET_ABI32 |
| 387 | env->idt.limit = 511; |
| 388 | #else |
| 389 | env->idt.limit = 255; |
| 390 | #endif |
| 391 | env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), |
| 392 | PROT_READ|PROT_WRITE, |
| 393 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); |
| 394 | idt_table = g2h_untagged(env->idt.base); |
| 395 | for (int i = 0; i < 20; i++) { |
| 396 | set_idt(i, 0, is64); |
| 397 | } |
| 398 | set_idt(3, 3, is64); |
| 399 | set_idt(4, 3, is64); |
| 400 | set_idt(0x80, 3, is64); |
| 401 | |
| 402 | /* linux segment setup */ |
| 403 | { |
| 404 | uint64_t *gdt_table; |
| 405 | env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, |
| 406 | PROT_READ|PROT_WRITE, |
| 407 | MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); |
| 408 | env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; |
| 409 | gdt_table = g2h_untagged(env->gdt.base); |
| 410 | #ifdef TARGET_ABI32 |
| 411 | write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, |
| 412 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
| 413 | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); |
| 414 | #else |
| 415 | /* 64 bit code segment */ |
| 416 | write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, |
| 417 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
| 418 | DESC_L_MASK | |
| 419 | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); |
| 420 | #endif |
| 421 | write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, |
| 422 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
| 423 | (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); |
| 424 | } |
| 425 | cpu_x86_load_seg(env, R_CS, __USER_CS); |
| 426 | cpu_x86_load_seg(env, R_SS, __USER_DS); |
| 427 | #ifdef TARGET_ABI32 |
| 428 | cpu_x86_load_seg(env, R_DS, __USER_DS); |
| 429 | cpu_x86_load_seg(env, R_ES, __USER_DS); |
| 430 | cpu_x86_load_seg(env, R_FS, __USER_DS); |
| 431 | cpu_x86_load_seg(env, R_GS, __USER_DS); |
| 432 | /* This hack makes Wine work... */ |
| 433 | env->segs[R_FS].selector = 0; |
| 434 | #else |
| 435 | cpu_x86_load_seg(env, R_DS, 0); |
| 436 | cpu_x86_load_seg(env, R_ES, 0); |
| 437 | cpu_x86_load_seg(env, R_FS, 0); |
| 438 | cpu_x86_load_seg(env, R_GS, 0); |
| 439 | #endif |
| 440 | } |