x86_64 linux user emulation


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3646 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/linux-user/main.c b/linux-user/main.c
index ac7a174..cfe2a0e 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -147,8 +147,31 @@
     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 @@
     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 @@
         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 @@
                                           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 @@
             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 @@
         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 @@
     {
         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 01d042a..ab252cf 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2521,6 +2521,41 @@
     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 @@
                 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 @@
 #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 @@
 
                 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 @@
                 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 333977a..c5d15a6 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -881,7 +881,7 @@
 #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 @@
 	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 f82589c..2a8d696 100644
--- a/linux-user/x86_64/syscall.h
+++ b/linux-user/x86_64/syscall.h
@@ -91,3 +91,8 @@
 };
 
 #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 910a4b4..2114cba 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 0a75e8c..841824f 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -971,6 +971,14 @@
 }
 #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 @@
         env->eip = (uint32_t)env->star;
     }
 }
+#endif
 
 void helper_sysret(int dflag)
 {
@@ -1143,18 +1152,23 @@
 {
     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