From d2fd1af76777687697674e7a49eeceac83907f3e Mon Sep 17 00:00:00 2001 From: bellard Date: Wed, 14 Nov 2007 18:08:56 +0000 Subject: [PATCH] x86_64 linux user emulation git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3646 c046a42c-6fe2-441c-8c8c-71466251a162 --- linux-user/main.c | 73 +++++++++++++++++++++++++--- linux-user/syscall.c | 97 ++++++++++++++++++++++--------------- linux-user/syscall_defs.h | 26 +++++++++- linux-user/x86_64/syscall.h | 5 ++ target-i386/cpu.h | 3 ++ target-i386/helper.c | 20 ++++++-- 6 files changed, 174 insertions(+), 50 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index ac7a174d97..cfe2a0e369 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -147,8 +147,31 @@ static void write_dt(void *ptr, unsigned long addr, unsigned long limit, p[1] = tswapl(e2); } +#if TARGET_X86_64 +uint64_t idt_table[512]; + +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, + uint64_t addr, unsigned int sel) +{ + unsigned int e1, e2; + uint32_t *p; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswapl(e1); + p[1] = tswapl(e2); + p[2] = addr >> 32; +} +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); +} +#else +uint64_t idt_table[256]; + static void set_gate(void *ptr, unsigned int type, unsigned int dpl, - unsigned long addr, unsigned int sel) + uint32_t addr, unsigned int sel) { unsigned int e1, e2; uint32_t *p; @@ -159,13 +182,12 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl, p[1] = tswapl(e2); } -uint64_t idt_table[256]; - /* only dpl matters as we do only user space emulation */ static void set_idt(int n, unsigned int dpl) { set_gate(idt_table + n, 0, dpl, 0, 0); } +#endif void cpu_loop(CPUX86State *env) { @@ -177,7 +199,7 @@ void cpu_loop(CPUX86State *env) trapnr = cpu_x86_exec(env); switch(trapnr) { case 0x80: - /* linux syscall */ + /* linux syscall from int $0x80 */ env->regs[R_EAX] = do_syscall(env, env->regs[R_EAX], env->regs[R_EBX], @@ -187,6 +209,20 @@ void cpu_loop(CPUX86State *env) env->regs[R_EDI], env->regs[R_EBP]); break; +#ifndef TARGET_ABI32 + case EXCP_SYSCALL: + /* linux syscall from syscall intruction */ + env->regs[R_EAX] = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9]); + env->eip = env->exception_next_eip; + break; +#endif case EXCP0B_NOSEG: case EXCP0C_STACK: info.si_signo = SIGBUS; @@ -196,6 +232,7 @@ void cpu_loop(CPUX86State *env) queue_signal(info.si_signo, &info); break; case EXCP0D_GPF: + /* XXX: potential problem if ABI32 */ #ifndef TARGET_X86_64 if (env->eflags & VM_MASK) { handle_vm86_fault(env); @@ -2075,12 +2112,18 @@ int main(int argc, char **argv) env->cr[4] |= CR4_OSFXSR_MASK; env->hflags |= HF_OSFXSR_MASK; } +#ifndef TARGET_ABI32 + /* enable 64 bit mode */ + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA; + env->hflags |= HF_LMA_MASK; +#endif /* flags setup : we activate the IRQs by default as in user mode */ env->eflags |= IF_MASK; /* linux register setup */ -#if defined(TARGET_X86_64) +#ifndef TARGET_ABI32 env->regs[R_EAX] = regs->rax; env->regs[R_EBX] = regs->rbx; env->regs[R_ECX] = regs->rcx; @@ -2131,24 +2174,38 @@ int main(int argc, char **argv) { uint64_t *gdt_table; gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES); - env->gdt.base = h2g(gdt_table); + env->gdt.base = h2g((unsigned long)gdt_table); env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; +#ifdef TARGET_ABI32 write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#else + /* 64 bit code segment */ + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + DESC_L_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#endif write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); } cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); +#ifdef TARGET_ABI32 cpu_x86_load_seg(env, R_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_SS, __USER_DS); cpu_x86_load_seg(env, R_FS, __USER_DS); cpu_x86_load_seg(env, R_GS, __USER_DS); - /* This hack makes Wine work... */ env->segs[R_FS].selector = 0; +#else + cpu_x86_load_seg(env, R_DS, 0); + cpu_x86_load_seg(env, R_ES, 0); + cpu_x86_load_seg(env, R_FS, 0); + cpu_x86_load_seg(env, R_GS, 0); +#endif #elif defined(TARGET_ARM) { int i; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 01d042a51f..ab252cfe89 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2521,6 +2521,41 @@ abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) return 0; } +#ifndef TARGET_ABI32 +abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) +{ + abi_long ret; + abi_ulong val; + int idx; + + switch(code) { + case TARGET_ARCH_SET_GS: + case TARGET_ARCH_SET_FS: + if (code == TARGET_ARCH_SET_GS) + idx = R_GS; + else + idx = R_FS; + cpu_x86_load_seg(env, idx, 0); + env->segs[idx].base = addr; + break; + case TARGET_ARCH_GET_GS: + case TARGET_ARCH_GET_FS: + if (code == TARGET_ARCH_GET_GS) + idx = R_GS; + else + idx = R_FS; + val = env->segs[idx].base; + if (put_user(val, addr, abi_ulong)) + return -TARGET_EFAULT; + break; + default: + ret = -TARGET_EINVAL; + break; + } + return 0; +} +#endif + #endif /* defined(TARGET_I386) */ /* this stack is the equivalent of the kernel stack associated with a @@ -2797,7 +2832,7 @@ void syscall_init(void) target_to_host_errno_table[host_to_target_errno_table[i]] = i; /* automatic consistency check if same arch */ -#if defined(__i386__) && defined(TARGET_I386) +#if defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32) if (ie->target_cmd != ie->host_cmd) { fprintf(stderr, "ERROR: ioctl: target=0x%x host=0x%x\n", ie->target_cmd, ie->host_cmd); @@ -3861,7 +3896,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) { abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; @@ -4183,42 +4218,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0)) goto efault; -#if defined(TARGET_MIPS) || (defined(TARGET_SPARC64) && !defined(TARGET_ABI32)) - target_st->st_dev = tswap32(st.st_dev); -#else - target_st->st_dev = tswap16(st.st_dev); -#endif - target_st->st_ino = tswapl(st.st_ino); -#if defined(TARGET_PPC) || defined(TARGET_MIPS) - target_st->st_mode = tswapl(st.st_mode); /* XXX: check this */ - target_st->st_uid = tswap32(st.st_uid); - target_st->st_gid = tswap32(st.st_gid); -#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - target_st->st_mode = tswap32(st.st_mode); - target_st->st_uid = tswap32(st.st_uid); - target_st->st_gid = tswap32(st.st_gid); -#else - target_st->st_mode = tswap16(st.st_mode); - target_st->st_uid = tswap16(st.st_uid); - target_st->st_gid = tswap16(st.st_gid); -#endif -#if defined(TARGET_MIPS) - /* If this is the same on PPC, then just merge w/ the above ifdef */ - target_st->st_nlink = tswapl(st.st_nlink); - target_st->st_rdev = tswapl(st.st_rdev); -#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - target_st->st_nlink = tswap32(st.st_nlink); - target_st->st_rdev = tswap32(st.st_rdev); -#else - target_st->st_nlink = tswap16(st.st_nlink); - target_st->st_rdev = tswap16(st.st_rdev); -#endif - target_st->st_size = tswapl(st.st_size); - target_st->st_blksize = tswapl(st.st_blksize); - target_st->st_blocks = tswapl(st.st_blocks); - target_st->target_st_atime = tswapl(st.st_atime); - target_st->target_st_mtime = tswapl(st.st_mtime); - target_st->target_st_ctime = tswapl(st.st_ctime); + __put_user(st.st_dev, &target_st->st_dev); + __put_user(st.st_ino, &target_st->st_ino); + __put_user(st.st_mode, &target_st->st_mode); + __put_user(st.st_uid, &target_st->st_uid); + __put_user(st.st_gid, &target_st->st_gid); + __put_user(st.st_nlink, &target_st->st_nlink); + __put_user(st.st_rdev, &target_st->st_rdev); + __put_user(st.st_size, &target_st->st_size); + __put_user(st.st_blksize, &target_st->st_blksize); + __put_user(st.st_blocks, &target_st->st_blocks); + __put_user(st.st_atime, &target_st->target_st_atime); + __put_user(st.st_mtime, &target_st->target_st_mtime); + __put_user(st.st_ctime, &target_st->target_st_ctime); unlock_user_struct(target_st, arg2, 1); } } @@ -4671,6 +4683,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } break; +#ifdef TARGET_NR_arch_prctl + case TARGET_NR_arch_prctl: +#if defined(TARGET_I386) && !defined(TARGET_ABI32) + ret = do_arch_prctl(cpu_env, arg1, arg2); + break; +#else + goto unimplemented; +#endif +#endif #ifdef TARGET_NR_pread case TARGET_NR_pread: if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 333977aa9f..c5d15a6f94 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -881,7 +881,7 @@ struct target_winsize { #define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ #endif -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_CRIS) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS) struct target_stat { unsigned short st_dev; unsigned short __pad1; @@ -1474,6 +1474,30 @@ struct target_stat64 { unsigned long long st_ino; }; +#elif defined(TARGET_I386) && !defined(TARGET_ABI32) +struct target_stat { + abi_ulong st_dev; + abi_ulong st_ino; + abi_ulong st_nlink; + + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad0; + abi_ulong st_rdev; + abi_long st_size; + abi_long st_blksize; + abi_long st_blocks; /* Number 512-byte blocks allocated. */ + + abi_ulong target_st_atime; + abi_ulong target_st_atime_nsec; + abi_ulong target_st_mtime; + abi_ulong target_st_mtime_nsec; + abi_ulong target_st_ctime; + abi_ulong target_st_ctime_nsec; + + abi_long __unused[3]; +}; #else #error unsupported CPU #endif diff --git a/linux-user/x86_64/syscall.h b/linux-user/x86_64/syscall.h index f82589c49c..2a8d696bf2 100644 --- a/linux-user/x86_64/syscall.h +++ b/linux-user/x86_64/syscall.h @@ -91,3 +91,8 @@ struct target_msqid64_ds { }; #define UNAME_MACHINE "x86_64" + +#define TARGET_ARCH_SET_GS 0x1001 +#define TARGET_ARCH_SET_FS 0x1002 +#define TARGET_ARCH_GET_FS 0x1003 +#define TARGET_ARCH_GET_GS 0x1004 diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 910a4b48ce..2114cba920 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -340,6 +340,9 @@ #define EXCP11_ALGN 17 #define EXCP12_MCHK 18 +#define EXCP_SYSCALL 0x100 /* only happens in user only emulation + for syscall instruction */ + enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */ diff --git a/target-i386/helper.c b/target-i386/helper.c index 0a75e8c335..841824fc9a 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -971,6 +971,14 @@ static void do_interrupt64(int intno, int is_int, int error_code, } #endif +#if defined(CONFIG_USER_ONLY) +void helper_syscall(int next_eip_addend) +{ + env->exception_index = EXCP_SYSCALL; + env->exception_next_eip = env->eip + next_eip_addend; + cpu_loop_exit(); +} +#else void helper_syscall(int next_eip_addend) { int selector; @@ -1024,6 +1032,7 @@ void helper_syscall(int next_eip_addend) env->eip = (uint32_t)env->star; } } +#endif void helper_sysret(int dflag) { @@ -1143,18 +1152,23 @@ void do_interrupt_user(int intno, int is_int, int error_code, { SegmentCache *dt; target_ulong ptr; - int dpl, cpl; + int dpl, cpl, shift; uint32_t e2; dt = &env->idt; - ptr = dt->base + (intno * 8); + if (env->hflags & HF_LMA_MASK) { + shift = 4; + } else { + shift = 3; + } + ptr = dt->base + (intno << shift); e2 = ldl_kernel(ptr + 4); dpl = (e2 >> DESC_DPL_SHIFT) & 3; cpl = env->hflags & HF_CPL_MASK; /* check privledge if software int */ if (is_int && dpl < cpl) - raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + raise_exception_err(EXCP0D_GPF, (intno << shift) + 2); /* Since we emulate only user space, we cannot do more than exiting the emulation with the suitable exception and error