x32: Handle process creation
authorH. Peter Anvin <hpa@zytor.com>
Sun, 19 Feb 2012 18:06:34 +0000 (10:06 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Mon, 20 Feb 2012 20:52:05 +0000 (12:52 -0800)
Allow an x32 process to be started.

Originally-by: H. J. Lu <hjl.tools@gmail.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
arch/x86/include/asm/compat.h
arch/x86/include/asm/elf.h
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/entry_64.S
arch/x86/kernel/process_64.c

index 7938b84e4506200c7f6afde771334cbff684c5d6..e7f68b49c01a6b183b9c3e803dcfd69cf1e3c602 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include <linux/types.h>
 #include <linux/sched.h>
+#include <asm/processor.h>
 #include <asm/user32.h>
 #include <asm/unistd.h>
 
@@ -187,7 +188,20 @@ struct compat_shmid64_ds {
 /*
  * The type of struct elf_prstatus.pr_reg in compatible core dumps.
  */
+#ifdef CONFIG_X86_X32_ABI
+typedef struct user_regs_struct compat_elf_gregset_t;
+
+#define PR_REG_SIZE(S) (test_thread_flag(TIF_IA32) ? 68 : 216)
+#define PRSTATUS_SIZE(S) (test_thread_flag(TIF_IA32) ? 144 : 296)
+#define SET_PR_FPVALID(S,V) \
+  do { *(int *) (((void *) &((S)->pr_reg)) + PR_REG_SIZE(0)) = (V); } \
+  while (0)
+
+#define COMPAT_USE_64BIT_TIME \
+       (!!(task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT))
+#else
 typedef struct user_regs_struct32 compat_elf_gregset_t;
+#endif
 
 /*
  * A pointer passed in from user mode. This should not
@@ -209,8 +223,16 @@ static inline compat_uptr_t ptr_to_compat(void __user *uptr)
 
 static inline void __user *arch_compat_alloc_user_space(long len)
 {
-       struct pt_regs *regs = task_pt_regs(current);
-       return (void __user *)regs->sp - len;
+       compat_uptr_t sp;
+
+       if (test_thread_flag(TIF_IA32)) {
+               sp = task_pt_regs(current)->sp;
+       } else {
+               /* -128 for the x32 ABI redzone */
+               sp = percpu_read(old_rsp) - 128;
+       }
+
+       return (void __user *)round_down(sp - len, 16);
 }
 
 static inline bool is_compat_task(void)
index 410fa6a219f6f24331db1e8ee3869f3f9dd12211..83aabea95dd77cfa482e48cd133db03ab2915cdb 100644 (file)
@@ -156,7 +156,12 @@ do {                                               \
 #define elf_check_arch(x)                      \
        ((x)->e_machine == EM_X86_64)
 
-#define compat_elf_check_arch(x)       elf_check_arch_ia32(x)
+#define compat_elf_check_arch(x)               \
+       (elf_check_arch_ia32(x) || (x)->e_machine == EM_X86_64)
+
+#if __USER32_DS != __USER_DS
+# error "The following code assumes __USER32_DS == __USER_DS"
+#endif
 
 static inline void elf_common_init(struct thread_struct *t,
                                   struct pt_regs *regs, const u16 ds)
@@ -179,8 +184,9 @@ static inline void elf_common_init(struct thread_struct *t,
 void start_thread_ia32(struct pt_regs *regs, u32 new_ip, u32 new_sp);
 #define compat_start_thread start_thread_ia32
 
-void set_personality_ia32(void);
-#define COMPAT_SET_PERSONALITY(ex) set_personality_ia32()
+void set_personality_ia32(bool);
+#define COMPAT_SET_PERSONALITY(ex)                     \
+       set_personality_ia32((ex).e_machine == EM_X86_64)
 
 #define COMPAT_ELF_PLATFORM                    ("i686")
 
@@ -296,9 +302,20 @@ do {                                                                       \
                            (unsigned long)current->mm->context.vdso);  \
 } while (0)
 
+#define ARCH_DLINFO_X32                                                        \
+do {                                                                   \
+       if (vdso_enabled)                                               \
+               NEW_AUX_ENT(AT_SYSINFO_EHDR,                            \
+                           (unsigned long)current->mm->context.vdso);  \
+} while (0)
+
 #define AT_SYSINFO             32
 
-#define COMPAT_ARCH_DLINFO     ARCH_DLINFO_IA32(sysctl_vsyscall32)
+#define COMPAT_ARCH_DLINFO                                             \
+if (test_thread_flag(TIF_X32))                                         \
+       ARCH_DLINFO_X32;                                                \
+else                                                                   \
+       ARCH_DLINFO_IA32(sysctl_vsyscall32)
 
 #define COMPAT_ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000)
 
index 5adce1040b118c597daf067e675896eb2100f808..63c0e058a40503f6a7d8f60a0b50471e7b6e437f 100644 (file)
@@ -28,7 +28,6 @@
 #include <asm/apic.h>
 #include <asm/stacktrace.h>
 #include <asm/nmi.h>
-#include <asm/compat.h>
 #include <asm/smp.h>
 #include <asm/alternative.h>
 
@@ -1595,6 +1594,9 @@ perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs)
 }
 
 #ifdef CONFIG_COMPAT
+
+#include <asm/compat.h>
+
 static inline int
 perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
 {
index 53dc821f0a629604d3ee04c91187dacb3e7f6f39..9e036f0ce5e0dc458fae8a62c782f7664bd10a78 100644 (file)
@@ -763,6 +763,21 @@ ENTRY(stub_x32_rt_sigreturn)
        CFI_ENDPROC
 END(stub_x32_rt_sigreturn)
 
+ENTRY(stub_x32_execve)
+       CFI_STARTPROC
+       addq $8, %rsp
+       PARTIAL_FRAME 0
+       SAVE_REST
+       FIXUP_TOP_OF_STACK %r11
+       movq %rsp, %rcx
+       call sys32_execve
+       RESTORE_TOP_OF_STACK %r11
+       movq %rax,RAX(%rsp)
+       RESTORE_REST
+       jmp int_ret_from_sys_call
+       CFI_ENDPROC
+END(stub_x32_execve)
+
 #endif
 
 /*
index 5fe2fbaa56ba264168f2d2c6d9ba53ed7d57a1ee..a0701da2bd180d6dd1f3d153b6f34c23a3f6fd32 100644 (file)
@@ -364,7 +364,9 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
 void start_thread_ia32(struct pt_regs *regs, u32 new_ip, u32 new_sp)
 {
        start_thread_common(regs, new_ip, new_sp,
-                           __USER32_CS, __USER32_DS, __USER32_DS);
+                           test_thread_flag(TIF_X32)
+                           ? __USER_CS : __USER32_CS,
+                           __USER_DS, __USER_DS);
 }
 #endif
 
@@ -508,6 +510,7 @@ void set_personality_64bit(void)
 
        /* Make sure to be in 64bit mode */
        clear_thread_flag(TIF_IA32);
+       clear_thread_flag(TIF_X32);
        clear_thread_flag(TIF_ADDR32);
        clear_thread_flag(TIF_X32);
 
@@ -522,22 +525,28 @@ void set_personality_64bit(void)
        current->personality &= ~READ_IMPLIES_EXEC;
 }
 
-void set_personality_ia32(void)
+void set_personality_ia32(bool x32)
 {
        /* inherit personality from parent */
 
        /* Make sure to be in 32bit mode */
-       set_thread_flag(TIF_IA32);
        set_thread_flag(TIF_ADDR32);
-       clear_thread_flag(TIF_X32);
-       current->personality |= force_personality32;
 
        /* Mark the associated mm as containing 32-bit tasks. */
        if (current->mm)
                current->mm->context.ia32_compat = 1;
 
-       /* Prepare the first "return" to user space */
-       current_thread_info()->status |= TS_COMPAT;
+       if (x32) {
+               clear_thread_flag(TIF_IA32);
+               set_thread_flag(TIF_X32);
+               current->personality &= ~READ_IMPLIES_EXEC;
+       } else {
+               set_thread_flag(TIF_IA32);
+               clear_thread_flag(TIF_X32);
+               current->personality |= force_personality32;
+               /* Prepare the first "return" to user space */
+               current_thread_info()->status |= TS_COMPAT;
+       }
 }
 
 unsigned long get_wchan(struct task_struct *p)