Commit d2fd1af76777687697674e7a49eeceac83907f3e
1 parent
6b23f777
x86_64 linux user emulation
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3646 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
174 additions
and
50 deletions
linux-user/main.c
| ... | ... | @@ -147,8 +147,11 @@ static void write_dt(void *ptr, unsigned long addr, unsigned long limit, |
| 147 | 147 | p[1] = tswapl(e2); |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | -static void set_gate(void *ptr, unsigned int type, unsigned int dpl, | |
| 151 | - unsigned long addr, unsigned int sel) | |
| 150 | +#if TARGET_X86_64 | |
| 151 | +uint64_t idt_table[512]; | |
| 152 | + | |
| 153 | +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, | |
| 154 | + uint64_t addr, unsigned int sel) | |
| 152 | 155 | { |
| 153 | 156 | unsigned int e1, e2; |
| 154 | 157 | uint32_t *p; |
| ... | ... | @@ -157,15 +160,34 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl, |
| 157 | 160 | p = ptr; |
| 158 | 161 | p[0] = tswapl(e1); |
| 159 | 162 | p[1] = tswapl(e2); |
| 163 | + p[2] = addr >> 32; | |
| 160 | 164 | } |
| 161 | - | |
| 165 | +/* only dpl matters as we do only user space emulation */ | |
| 166 | +static void set_idt(int n, unsigned int dpl) | |
| 167 | +{ | |
| 168 | + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); | |
| 169 | +} | |
| 170 | +#else | |
| 162 | 171 | uint64_t idt_table[256]; |
| 163 | 172 | |
| 173 | +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, | |
| 174 | + uint32_t addr, unsigned int sel) | |
| 175 | +{ | |
| 176 | + unsigned int e1, e2; | |
| 177 | + uint32_t *p; | |
| 178 | + e1 = (addr & 0xffff) | (sel << 16); | |
| 179 | + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); | |
| 180 | + p = ptr; | |
| 181 | + p[0] = tswapl(e1); | |
| 182 | + p[1] = tswapl(e2); | |
| 183 | +} | |
| 184 | + | |
| 164 | 185 | /* only dpl matters as we do only user space emulation */ |
| 165 | 186 | static void set_idt(int n, unsigned int dpl) |
| 166 | 187 | { |
| 167 | 188 | set_gate(idt_table + n, 0, dpl, 0, 0); |
| 168 | 189 | } |
| 190 | +#endif | |
| 169 | 191 | |
| 170 | 192 | void cpu_loop(CPUX86State *env) |
| 171 | 193 | { |
| ... | ... | @@ -177,7 +199,7 @@ void cpu_loop(CPUX86State *env) |
| 177 | 199 | trapnr = cpu_x86_exec(env); |
| 178 | 200 | switch(trapnr) { |
| 179 | 201 | case 0x80: |
| 180 | - /* linux syscall */ | |
| 202 | + /* linux syscall from int $0x80 */ | |
| 181 | 203 | env->regs[R_EAX] = do_syscall(env, |
| 182 | 204 | env->regs[R_EAX], |
| 183 | 205 | env->regs[R_EBX], |
| ... | ... | @@ -187,6 +209,20 @@ void cpu_loop(CPUX86State *env) |
| 187 | 209 | env->regs[R_EDI], |
| 188 | 210 | env->regs[R_EBP]); |
| 189 | 211 | break; |
| 212 | +#ifndef TARGET_ABI32 | |
| 213 | + case EXCP_SYSCALL: | |
| 214 | + /* linux syscall from syscall intruction */ | |
| 215 | + env->regs[R_EAX] = do_syscall(env, | |
| 216 | + env->regs[R_EAX], | |
| 217 | + env->regs[R_EDI], | |
| 218 | + env->regs[R_ESI], | |
| 219 | + env->regs[R_EDX], | |
| 220 | + env->regs[10], | |
| 221 | + env->regs[8], | |
| 222 | + env->regs[9]); | |
| 223 | + env->eip = env->exception_next_eip; | |
| 224 | + break; | |
| 225 | +#endif | |
| 190 | 226 | case EXCP0B_NOSEG: |
| 191 | 227 | case EXCP0C_STACK: |
| 192 | 228 | info.si_signo = SIGBUS; |
| ... | ... | @@ -196,6 +232,7 @@ void cpu_loop(CPUX86State *env) |
| 196 | 232 | queue_signal(info.si_signo, &info); |
| 197 | 233 | break; |
| 198 | 234 | case EXCP0D_GPF: |
| 235 | + /* XXX: potential problem if ABI32 */ | |
| 199 | 236 | #ifndef TARGET_X86_64 |
| 200 | 237 | if (env->eflags & VM_MASK) { |
| 201 | 238 | handle_vm86_fault(env); |
| ... | ... | @@ -2075,12 +2112,18 @@ int main(int argc, char **argv) |
| 2075 | 2112 | env->cr[4] |= CR4_OSFXSR_MASK; |
| 2076 | 2113 | env->hflags |= HF_OSFXSR_MASK; |
| 2077 | 2114 | } |
| 2115 | +#ifndef TARGET_ABI32 | |
| 2116 | + /* enable 64 bit mode */ | |
| 2117 | + env->cr[4] |= CR4_PAE_MASK; | |
| 2118 | + env->efer |= MSR_EFER_LMA; | |
| 2119 | + env->hflags |= HF_LMA_MASK; | |
| 2120 | +#endif | |
| 2078 | 2121 | |
| 2079 | 2122 | /* flags setup : we activate the IRQs by default as in user mode */ |
| 2080 | 2123 | env->eflags |= IF_MASK; |
| 2081 | 2124 | |
| 2082 | 2125 | /* linux register setup */ |
| 2083 | -#if defined(TARGET_X86_64) | |
| 2126 | +#ifndef TARGET_ABI32 | |
| 2084 | 2127 | env->regs[R_EAX] = regs->rax; |
| 2085 | 2128 | env->regs[R_EBX] = regs->rbx; |
| 2086 | 2129 | env->regs[R_ECX] = regs->rcx; |
| ... | ... | @@ -2131,24 +2174,38 @@ int main(int argc, char **argv) |
| 2131 | 2174 | { |
| 2132 | 2175 | uint64_t *gdt_table; |
| 2133 | 2176 | gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES); |
| 2134 | - env->gdt.base = h2g(gdt_table); | |
| 2177 | + env->gdt.base = h2g((unsigned long)gdt_table); | |
| 2135 | 2178 | env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; |
| 2179 | +#ifdef TARGET_ABI32 | |
| 2136 | 2180 | write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, |
| 2137 | 2181 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
| 2138 | 2182 | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); |
| 2183 | +#else | |
| 2184 | + /* 64 bit code segment */ | |
| 2185 | + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, | |
| 2186 | + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | | |
| 2187 | + DESC_L_MASK | | |
| 2188 | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); | |
| 2189 | +#endif | |
| 2139 | 2190 | write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, |
| 2140 | 2191 | DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | |
| 2141 | 2192 | (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); |
| 2142 | 2193 | } |
| 2143 | 2194 | cpu_x86_load_seg(env, R_CS, __USER_CS); |
| 2195 | + cpu_x86_load_seg(env, R_SS, __USER_DS); | |
| 2196 | +#ifdef TARGET_ABI32 | |
| 2144 | 2197 | cpu_x86_load_seg(env, R_DS, __USER_DS); |
| 2145 | 2198 | cpu_x86_load_seg(env, R_ES, __USER_DS); |
| 2146 | - cpu_x86_load_seg(env, R_SS, __USER_DS); | |
| 2147 | 2199 | cpu_x86_load_seg(env, R_FS, __USER_DS); |
| 2148 | 2200 | cpu_x86_load_seg(env, R_GS, __USER_DS); |
| 2149 | - | |
| 2150 | 2201 | /* This hack makes Wine work... */ |
| 2151 | 2202 | env->segs[R_FS].selector = 0; |
| 2203 | +#else | |
| 2204 | + cpu_x86_load_seg(env, R_DS, 0); | |
| 2205 | + cpu_x86_load_seg(env, R_ES, 0); | |
| 2206 | + cpu_x86_load_seg(env, R_FS, 0); | |
| 2207 | + cpu_x86_load_seg(env, R_GS, 0); | |
| 2208 | +#endif | |
| 2152 | 2209 | #elif defined(TARGET_ARM) |
| 2153 | 2210 | { |
| 2154 | 2211 | int i; | ... | ... |
linux-user/syscall.c
| ... | ... | @@ -2521,6 +2521,41 @@ abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) |
| 2521 | 2521 | return 0; |
| 2522 | 2522 | } |
| 2523 | 2523 | |
| 2524 | +#ifndef TARGET_ABI32 | |
| 2525 | +abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) | |
| 2526 | +{ | |
| 2527 | + abi_long ret; | |
| 2528 | + abi_ulong val; | |
| 2529 | + int idx; | |
| 2530 | + | |
| 2531 | + switch(code) { | |
| 2532 | + case TARGET_ARCH_SET_GS: | |
| 2533 | + case TARGET_ARCH_SET_FS: | |
| 2534 | + if (code == TARGET_ARCH_SET_GS) | |
| 2535 | + idx = R_GS; | |
| 2536 | + else | |
| 2537 | + idx = R_FS; | |
| 2538 | + cpu_x86_load_seg(env, idx, 0); | |
| 2539 | + env->segs[idx].base = addr; | |
| 2540 | + break; | |
| 2541 | + case TARGET_ARCH_GET_GS: | |
| 2542 | + case TARGET_ARCH_GET_FS: | |
| 2543 | + if (code == TARGET_ARCH_GET_GS) | |
| 2544 | + idx = R_GS; | |
| 2545 | + else | |
| 2546 | + idx = R_FS; | |
| 2547 | + val = env->segs[idx].base; | |
| 2548 | + if (put_user(val, addr, abi_ulong)) | |
| 2549 | + return -TARGET_EFAULT; | |
| 2550 | + break; | |
| 2551 | + default: | |
| 2552 | + ret = -TARGET_EINVAL; | |
| 2553 | + break; | |
| 2554 | + } | |
| 2555 | + return 0; | |
| 2556 | +} | |
| 2557 | +#endif | |
| 2558 | + | |
| 2524 | 2559 | #endif /* defined(TARGET_I386) */ |
| 2525 | 2560 | |
| 2526 | 2561 | /* this stack is the equivalent of the kernel stack associated with a |
| ... | ... | @@ -2797,7 +2832,7 @@ void syscall_init(void) |
| 2797 | 2832 | target_to_host_errno_table[host_to_target_errno_table[i]] = i; |
| 2798 | 2833 | |
| 2799 | 2834 | /* automatic consistency check if same arch */ |
| 2800 | -#if defined(__i386__) && defined(TARGET_I386) | |
| 2835 | +#if defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32) | |
| 2801 | 2836 | if (ie->target_cmd != ie->host_cmd) { |
| 2802 | 2837 | fprintf(stderr, "ERROR: ioctl: target=0x%x host=0x%x\n", |
| 2803 | 2838 | ie->target_cmd, ie->host_cmd); |
| ... | ... | @@ -3861,7 +3896,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 3861 | 3896 | #endif |
| 3862 | 3897 | #ifdef TARGET_NR_mmap |
| 3863 | 3898 | case TARGET_NR_mmap: |
| 3864 | -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) | |
| 3899 | +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) | |
| 3865 | 3900 | { |
| 3866 | 3901 | abi_ulong *v; |
| 3867 | 3902 | abi_ulong v1, v2, v3, v4, v5, v6; |
| ... | ... | @@ -4183,42 +4218,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 4183 | 4218 | |
| 4184 | 4219 | if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0)) |
| 4185 | 4220 | goto efault; |
| 4186 | -#if defined(TARGET_MIPS) || (defined(TARGET_SPARC64) && !defined(TARGET_ABI32)) | |
| 4187 | - target_st->st_dev = tswap32(st.st_dev); | |
| 4188 | -#else | |
| 4189 | - target_st->st_dev = tswap16(st.st_dev); | |
| 4190 | -#endif | |
| 4191 | - target_st->st_ino = tswapl(st.st_ino); | |
| 4192 | -#if defined(TARGET_PPC) || defined(TARGET_MIPS) | |
| 4193 | - target_st->st_mode = tswapl(st.st_mode); /* XXX: check this */ | |
| 4194 | - target_st->st_uid = tswap32(st.st_uid); | |
| 4195 | - target_st->st_gid = tswap32(st.st_gid); | |
| 4196 | -#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32) | |
| 4197 | - target_st->st_mode = tswap32(st.st_mode); | |
| 4198 | - target_st->st_uid = tswap32(st.st_uid); | |
| 4199 | - target_st->st_gid = tswap32(st.st_gid); | |
| 4200 | -#else | |
| 4201 | - target_st->st_mode = tswap16(st.st_mode); | |
| 4202 | - target_st->st_uid = tswap16(st.st_uid); | |
| 4203 | - target_st->st_gid = tswap16(st.st_gid); | |
| 4204 | -#endif | |
| 4205 | -#if defined(TARGET_MIPS) | |
| 4206 | - /* If this is the same on PPC, then just merge w/ the above ifdef */ | |
| 4207 | - target_st->st_nlink = tswapl(st.st_nlink); | |
| 4208 | - target_st->st_rdev = tswapl(st.st_rdev); | |
| 4209 | -#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32) | |
| 4210 | - target_st->st_nlink = tswap32(st.st_nlink); | |
| 4211 | - target_st->st_rdev = tswap32(st.st_rdev); | |
| 4212 | -#else | |
| 4213 | - target_st->st_nlink = tswap16(st.st_nlink); | |
| 4214 | - target_st->st_rdev = tswap16(st.st_rdev); | |
| 4215 | -#endif | |
| 4216 | - target_st->st_size = tswapl(st.st_size); | |
| 4217 | - target_st->st_blksize = tswapl(st.st_blksize); | |
| 4218 | - target_st->st_blocks = tswapl(st.st_blocks); | |
| 4219 | - target_st->target_st_atime = tswapl(st.st_atime); | |
| 4220 | - target_st->target_st_mtime = tswapl(st.st_mtime); | |
| 4221 | - target_st->target_st_ctime = tswapl(st.st_ctime); | |
| 4221 | + __put_user(st.st_dev, &target_st->st_dev); | |
| 4222 | + __put_user(st.st_ino, &target_st->st_ino); | |
| 4223 | + __put_user(st.st_mode, &target_st->st_mode); | |
| 4224 | + __put_user(st.st_uid, &target_st->st_uid); | |
| 4225 | + __put_user(st.st_gid, &target_st->st_gid); | |
| 4226 | + __put_user(st.st_nlink, &target_st->st_nlink); | |
| 4227 | + __put_user(st.st_rdev, &target_st->st_rdev); | |
| 4228 | + __put_user(st.st_size, &target_st->st_size); | |
| 4229 | + __put_user(st.st_blksize, &target_st->st_blksize); | |
| 4230 | + __put_user(st.st_blocks, &target_st->st_blocks); | |
| 4231 | + __put_user(st.st_atime, &target_st->target_st_atime); | |
| 4232 | + __put_user(st.st_mtime, &target_st->target_st_mtime); | |
| 4233 | + __put_user(st.st_ctime, &target_st->target_st_ctime); | |
| 4222 | 4234 | unlock_user_struct(target_st, arg2, 1); |
| 4223 | 4235 | } |
| 4224 | 4236 | } |
| ... | ... | @@ -4671,6 +4683,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 4671 | 4683 | break; |
| 4672 | 4684 | } |
| 4673 | 4685 | break; |
| 4686 | +#ifdef TARGET_NR_arch_prctl | |
| 4687 | + case TARGET_NR_arch_prctl: | |
| 4688 | +#if defined(TARGET_I386) && !defined(TARGET_ABI32) | |
| 4689 | + ret = do_arch_prctl(cpu_env, arg1, arg2); | |
| 4690 | + break; | |
| 4691 | +#else | |
| 4692 | + goto unimplemented; | |
| 4693 | +#endif | |
| 4694 | +#endif | |
| 4674 | 4695 | #ifdef TARGET_NR_pread |
| 4675 | 4696 | case TARGET_NR_pread: |
| 4676 | 4697 | if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) | ... | ... |
linux-user/syscall_defs.h
| ... | ... | @@ -881,7 +881,7 @@ struct target_winsize { |
| 881 | 881 | #define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ |
| 882 | 882 | #endif |
| 883 | 883 | |
| 884 | -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_CRIS) | |
| 884 | +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS) | |
| 885 | 885 | struct target_stat { |
| 886 | 886 | unsigned short st_dev; |
| 887 | 887 | unsigned short __pad1; |
| ... | ... | @@ -1474,6 +1474,30 @@ struct target_stat64 { |
| 1474 | 1474 | unsigned long long st_ino; |
| 1475 | 1475 | }; |
| 1476 | 1476 | |
| 1477 | +#elif defined(TARGET_I386) && !defined(TARGET_ABI32) | |
| 1478 | +struct target_stat { | |
| 1479 | + abi_ulong st_dev; | |
| 1480 | + abi_ulong st_ino; | |
| 1481 | + abi_ulong st_nlink; | |
| 1482 | + | |
| 1483 | + unsigned int st_mode; | |
| 1484 | + unsigned int st_uid; | |
| 1485 | + unsigned int st_gid; | |
| 1486 | + unsigned int __pad0; | |
| 1487 | + abi_ulong st_rdev; | |
| 1488 | + abi_long st_size; | |
| 1489 | + abi_long st_blksize; | |
| 1490 | + abi_long st_blocks; /* Number 512-byte blocks allocated. */ | |
| 1491 | + | |
| 1492 | + abi_ulong target_st_atime; | |
| 1493 | + abi_ulong target_st_atime_nsec; | |
| 1494 | + abi_ulong target_st_mtime; | |
| 1495 | + abi_ulong target_st_mtime_nsec; | |
| 1496 | + abi_ulong target_st_ctime; | |
| 1497 | + abi_ulong target_st_ctime_nsec; | |
| 1498 | + | |
| 1499 | + abi_long __unused[3]; | |
| 1500 | +}; | |
| 1477 | 1501 | #else |
| 1478 | 1502 | #error unsupported CPU |
| 1479 | 1503 | #endif | ... | ... |
linux-user/x86_64/syscall.h
target-i386/cpu.h
| ... | ... | @@ -340,6 +340,9 @@ |
| 340 | 340 | #define EXCP11_ALGN 17 |
| 341 | 341 | #define EXCP12_MCHK 18 |
| 342 | 342 | |
| 343 | +#define EXCP_SYSCALL 0x100 /* only happens in user only emulation | |
| 344 | + for syscall instruction */ | |
| 345 | + | |
| 343 | 346 | enum { |
| 344 | 347 | CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ |
| 345 | 348 | CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */ | ... | ... |
target-i386/helper.c
| ... | ... | @@ -971,6 +971,14 @@ static void do_interrupt64(int intno, int is_int, int error_code, |
| 971 | 971 | } |
| 972 | 972 | #endif |
| 973 | 973 | |
| 974 | +#if defined(CONFIG_USER_ONLY) | |
| 975 | +void helper_syscall(int next_eip_addend) | |
| 976 | +{ | |
| 977 | + env->exception_index = EXCP_SYSCALL; | |
| 978 | + env->exception_next_eip = env->eip + next_eip_addend; | |
| 979 | + cpu_loop_exit(); | |
| 980 | +} | |
| 981 | +#else | |
| 974 | 982 | void helper_syscall(int next_eip_addend) |
| 975 | 983 | { |
| 976 | 984 | int selector; |
| ... | ... | @@ -1024,6 +1032,7 @@ void helper_syscall(int next_eip_addend) |
| 1024 | 1032 | env->eip = (uint32_t)env->star; |
| 1025 | 1033 | } |
| 1026 | 1034 | } |
| 1035 | +#endif | |
| 1027 | 1036 | |
| 1028 | 1037 | void helper_sysret(int dflag) |
| 1029 | 1038 | { |
| ... | ... | @@ -1143,18 +1152,23 @@ void do_interrupt_user(int intno, int is_int, int error_code, |
| 1143 | 1152 | { |
| 1144 | 1153 | SegmentCache *dt; |
| 1145 | 1154 | target_ulong ptr; |
| 1146 | - int dpl, cpl; | |
| 1155 | + int dpl, cpl, shift; | |
| 1147 | 1156 | uint32_t e2; |
| 1148 | 1157 | |
| 1149 | 1158 | dt = &env->idt; |
| 1150 | - ptr = dt->base + (intno * 8); | |
| 1159 | + if (env->hflags & HF_LMA_MASK) { | |
| 1160 | + shift = 4; | |
| 1161 | + } else { | |
| 1162 | + shift = 3; | |
| 1163 | + } | |
| 1164 | + ptr = dt->base + (intno << shift); | |
| 1151 | 1165 | e2 = ldl_kernel(ptr + 4); |
| 1152 | 1166 | |
| 1153 | 1167 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
| 1154 | 1168 | cpl = env->hflags & HF_CPL_MASK; |
| 1155 | 1169 | /* check privledge if software int */ |
| 1156 | 1170 | if (is_int && dpl < cpl) |
| 1157 | - raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | |
| 1171 | + raise_exception_err(EXCP0D_GPF, (intno << shift) + 2); | |
| 1158 | 1172 | |
| 1159 | 1173 | /* Since we emulate only user space, we cannot do more than |
| 1160 | 1174 | exiting the emulation with the suitable exception and error | ... | ... |