diff options
Diffstat (limited to 'arch/x86_64/ia32')
-rw-r--r-- | arch/x86_64/ia32/Makefile | 32 | ||||
-rw-r--r-- | arch/x86_64/ia32/fpu32.c | 184 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32_aout.c | 529 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32_binfmt.c | 434 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32_ioctl.c | 201 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32_signal.c | 621 | ||||
-rw-r--r-- | arch/x86_64/ia32/ia32entry.S | 602 | ||||
-rw-r--r-- | arch/x86_64/ia32/ipc32.c | 57 | ||||
-rw-r--r-- | arch/x86_64/ia32/ptrace32.c | 379 | ||||
-rw-r--r-- | arch/x86_64/ia32/sys_ia32.c | 1050 | ||||
-rw-r--r-- | arch/x86_64/ia32/syscall32.c | 111 | ||||
-rw-r--r-- | arch/x86_64/ia32/tls32.c | 163 | ||||
-rw-r--r-- | arch/x86_64/ia32/vsyscall-sigreturn.S | 120 | ||||
-rw-r--r-- | arch/x86_64/ia32/vsyscall-syscall.S | 68 | ||||
-rw-r--r-- | arch/x86_64/ia32/vsyscall-sysenter.S | 94 | ||||
-rw-r--r-- | arch/x86_64/ia32/vsyscall.lds | 77 |
16 files changed, 4722 insertions, 0 deletions
diff --git a/arch/x86_64/ia32/Makefile b/arch/x86_64/ia32/Makefile new file mode 100644 index 000000000000..a12b19da4b59 --- /dev/null +++ b/arch/x86_64/ia32/Makefile | |||
@@ -0,0 +1,32 @@ | |||
1 | # | ||
2 | # Makefile for the ia32 kernel emulation subsystem. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_ioctl.o \ | ||
6 | ia32_signal.o tls32.o \ | ||
7 | ia32_binfmt.o fpu32.o ptrace32.o syscall32.o | ||
8 | |||
9 | sysv-$(CONFIG_SYSVIPC) := ipc32.o | ||
10 | obj-$(CONFIG_IA32_EMULATION) += $(sysv-y) | ||
11 | |||
12 | obj-$(CONFIG_IA32_AOUT) += ia32_aout.o | ||
13 | |||
14 | $(obj)/syscall32.o: $(src)/syscall32.c \ | ||
15 | $(foreach F,sysenter syscall,$(obj)/vsyscall-$F.so) | ||
16 | |||
17 | # Teach kbuild about targets | ||
18 | targets := $(foreach F,sysenter syscall,vsyscall-$F.o vsyscall-$F.so) | ||
19 | |||
20 | # The DSO images are built using a special linker script | ||
21 | quiet_cmd_syscall = SYSCALL $@ | ||
22 | cmd_syscall = $(CC) -m32 -nostdlib -shared -s \ | ||
23 | -Wl,-soname=linux-gate.so.1 -o $@ \ | ||
24 | -Wl,-T,$(filter-out FORCE,$^) | ||
25 | |||
26 | $(obj)/vsyscall-sysenter.so $(obj)/vsyscall-syscall.so: \ | ||
27 | $(obj)/vsyscall-%.so: $(src)/vsyscall.lds $(obj)/vsyscall-%.o FORCE | ||
28 | $(call if_changed,syscall) | ||
29 | |||
30 | AFLAGS_vsyscall-sysenter.o = -m32 | ||
31 | AFLAGS_vsyscall-syscall.o = -m32 | ||
32 | CFLAGS_ia32_ioctl.o += -Ifs/ | ||
diff --git a/arch/x86_64/ia32/fpu32.c b/arch/x86_64/ia32/fpu32.c new file mode 100644 index 000000000000..1c23095f1813 --- /dev/null +++ b/arch/x86_64/ia32/fpu32.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * Copyright 2002 Andi Kleen, SuSE Labs. | ||
3 | * FXSAVE<->i387 conversion support. Based on code by Gareth Hughes. | ||
4 | * This is used for ptrace, signals and coredumps in 32bit emulation. | ||
5 | * $Id: fpu32.c,v 1.1 2002/03/21 14:16:32 ak Exp $ | ||
6 | */ | ||
7 | |||
8 | #include <linux/sched.h> | ||
9 | #include <asm/sigcontext32.h> | ||
10 | #include <asm/processor.h> | ||
11 | #include <asm/uaccess.h> | ||
12 | #include <asm/i387.h> | ||
13 | |||
14 | static inline unsigned short twd_i387_to_fxsr(unsigned short twd) | ||
15 | { | ||
16 | unsigned int tmp; /* to avoid 16 bit prefixes in the code */ | ||
17 | |||
18 | /* Transform each pair of bits into 01 (valid) or 00 (empty) */ | ||
19 | tmp = ~twd; | ||
20 | tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ | ||
21 | /* and move the valid bits to the lower byte. */ | ||
22 | tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ | ||
23 | tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ | ||
24 | tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ | ||
25 | return tmp; | ||
26 | } | ||
27 | |||
28 | static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave) | ||
29 | { | ||
30 | struct _fpxreg *st = NULL; | ||
31 | unsigned long tos = (fxsave->swd >> 11) & 7; | ||
32 | unsigned long twd = (unsigned long) fxsave->twd; | ||
33 | unsigned long tag; | ||
34 | unsigned long ret = 0xffff0000; | ||
35 | int i; | ||
36 | |||
37 | #define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16); | ||
38 | |||
39 | for (i = 0 ; i < 8 ; i++) { | ||
40 | if (twd & 0x1) { | ||
41 | st = FPREG_ADDR( fxsave, (i - tos) & 7 ); | ||
42 | |||
43 | switch (st->exponent & 0x7fff) { | ||
44 | case 0x7fff: | ||
45 | tag = 2; /* Special */ | ||
46 | break; | ||
47 | case 0x0000: | ||
48 | if ( !st->significand[0] && | ||
49 | !st->significand[1] && | ||
50 | !st->significand[2] && | ||
51 | !st->significand[3] ) { | ||
52 | tag = 1; /* Zero */ | ||
53 | } else { | ||
54 | tag = 2; /* Special */ | ||
55 | } | ||
56 | break; | ||
57 | default: | ||
58 | if (st->significand[3] & 0x8000) { | ||
59 | tag = 0; /* Valid */ | ||
60 | } else { | ||
61 | tag = 2; /* Special */ | ||
62 | } | ||
63 | break; | ||
64 | } | ||
65 | } else { | ||
66 | tag = 3; /* Empty */ | ||
67 | } | ||
68 | ret |= (tag << (2 * i)); | ||
69 | twd = twd >> 1; | ||
70 | } | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | |||
75 | static inline int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave, | ||
76 | struct _fpstate_ia32 __user *buf) | ||
77 | { | ||
78 | struct _fpxreg *to; | ||
79 | struct _fpreg __user *from; | ||
80 | int i; | ||
81 | u32 v; | ||
82 | int err = 0; | ||
83 | |||
84 | #define G(num,val) err |= __get_user(val, num + (u32 __user *)buf) | ||
85 | G(0, fxsave->cwd); | ||
86 | G(1, fxsave->swd); | ||
87 | G(2, fxsave->twd); | ||
88 | fxsave->twd = twd_i387_to_fxsr(fxsave->twd); | ||
89 | G(3, fxsave->rip); | ||
90 | G(4, v); | ||
91 | fxsave->fop = v>>16; /* cs ignored */ | ||
92 | G(5, fxsave->rdp); | ||
93 | /* 6: ds ignored */ | ||
94 | #undef G | ||
95 | if (err) | ||
96 | return -1; | ||
97 | |||
98 | to = (struct _fpxreg *)&fxsave->st_space[0]; | ||
99 | from = &buf->_st[0]; | ||
100 | for (i = 0 ; i < 8 ; i++, to++, from++) { | ||
101 | if (__copy_from_user(to, from, sizeof(*from))) | ||
102 | return -1; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | |||
108 | static inline int convert_fxsr_to_user(struct _fpstate_ia32 __user *buf, | ||
109 | struct i387_fxsave_struct *fxsave, | ||
110 | struct pt_regs *regs, | ||
111 | struct task_struct *tsk) | ||
112 | { | ||
113 | struct _fpreg __user *to; | ||
114 | struct _fpxreg *from; | ||
115 | int i; | ||
116 | u16 cs,ds; | ||
117 | int err = 0; | ||
118 | |||
119 | if (tsk == current) { | ||
120 | /* should be actually ds/cs at fpu exception time, | ||
121 | but that information is not available in 64bit mode. */ | ||
122 | asm("movw %%ds,%0 " : "=r" (ds)); | ||
123 | asm("movw %%cs,%0 " : "=r" (cs)); | ||
124 | } else { /* ptrace. task has stopped. */ | ||
125 | ds = tsk->thread.ds; | ||
126 | cs = regs->cs; | ||
127 | } | ||
128 | |||
129 | #define P(num,val) err |= __put_user(val, num + (u32 __user *)buf) | ||
130 | P(0, (u32)fxsave->cwd | 0xffff0000); | ||
131 | P(1, (u32)fxsave->swd | 0xffff0000); | ||
132 | P(2, twd_fxsr_to_i387(fxsave)); | ||
133 | P(3, (u32)fxsave->rip); | ||
134 | P(4, cs | ((u32)fxsave->fop) << 16); | ||
135 | P(5, fxsave->rdp); | ||
136 | P(6, 0xffff0000 | ds); | ||
137 | #undef P | ||
138 | |||
139 | if (err) | ||
140 | return -1; | ||
141 | |||
142 | to = &buf->_st[0]; | ||
143 | from = (struct _fpxreg *) &fxsave->st_space[0]; | ||
144 | for ( i = 0 ; i < 8 ; i++, to++, from++ ) { | ||
145 | if (__copy_to_user(to, from, sizeof(*to))) | ||
146 | return -1; | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int restore_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 __user *buf, int fsave) | ||
152 | { | ||
153 | clear_fpu(tsk); | ||
154 | if (!fsave) { | ||
155 | if (__copy_from_user(&tsk->thread.i387.fxsave, | ||
156 | &buf->_fxsr_env[0], | ||
157 | sizeof(struct i387_fxsave_struct))) | ||
158 | return -1; | ||
159 | tsk->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | ||
160 | set_stopped_child_used_math(tsk); | ||
161 | } | ||
162 | return convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf); | ||
163 | } | ||
164 | |||
165 | int save_i387_ia32(struct task_struct *tsk, | ||
166 | struct _fpstate_ia32 __user *buf, | ||
167 | struct pt_regs *regs, | ||
168 | int fsave) | ||
169 | { | ||
170 | int err = 0; | ||
171 | |||
172 | init_fpu(tsk); | ||
173 | if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave, regs, tsk)) | ||
174 | return -1; | ||
175 | if (fsave) | ||
176 | return 0; | ||
177 | err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status); | ||
178 | if (fsave) | ||
179 | return err ? -1 : 1; | ||
180 | err |= __put_user(X86_FXSR_MAGIC, &buf->magic); | ||
181 | err |= __copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave, | ||
182 | sizeof(struct i387_fxsave_struct)); | ||
183 | return err ? -1 : 1; | ||
184 | } | ||
diff --git a/arch/x86_64/ia32/ia32_aout.c b/arch/x86_64/ia32/ia32_aout.c new file mode 100644 index 000000000000..1965efc974dc --- /dev/null +++ b/arch/x86_64/ia32/ia32_aout.c | |||
@@ -0,0 +1,529 @@ | |||
1 | /* | ||
2 | * a.out loader for x86-64 | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1996 Linus Torvalds | ||
5 | * Hacked together by Andi Kleen | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | |||
10 | #include <linux/time.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/mman.h> | ||
14 | #include <linux/a.out.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/signal.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/file.h> | ||
20 | #include <linux/stat.h> | ||
21 | #include <linux/fcntl.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/user.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/binfmts.h> | ||
26 | #include <linux/personality.h> | ||
27 | #include <linux/init.h> | ||
28 | |||
29 | #include <asm/system.h> | ||
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/pgalloc.h> | ||
32 | #include <asm/cacheflush.h> | ||
33 | #include <asm/user32.h> | ||
34 | #include <asm/ia32.h> | ||
35 | |||
36 | #undef WARN_OLD | ||
37 | #undef CORE_DUMP /* probably broken */ | ||
38 | |||
39 | extern int ia32_setup_arg_pages(struct linux_binprm *bprm, | ||
40 | unsigned long stack_top, int exec_stack); | ||
41 | |||
42 | static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs); | ||
43 | static int load_aout_library(struct file*); | ||
44 | |||
45 | #if CORE_DUMP | ||
46 | static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file); | ||
47 | |||
48 | /* | ||
49 | * fill in the user structure for a core dump.. | ||
50 | */ | ||
51 | static void dump_thread32(struct pt_regs * regs, struct user32 * dump) | ||
52 | { | ||
53 | u32 fs,gs; | ||
54 | |||
55 | /* changed the size calculations - should hopefully work better. lbt */ | ||
56 | dump->magic = CMAGIC; | ||
57 | dump->start_code = 0; | ||
58 | dump->start_stack = regs->rsp & ~(PAGE_SIZE - 1); | ||
59 | dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; | ||
60 | dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; | ||
61 | dump->u_dsize -= dump->u_tsize; | ||
62 | dump->u_ssize = 0; | ||
63 | dump->u_debugreg[0] = current->thread.debugreg0; | ||
64 | dump->u_debugreg[1] = current->thread.debugreg1; | ||
65 | dump->u_debugreg[2] = current->thread.debugreg2; | ||
66 | dump->u_debugreg[3] = current->thread.debugreg3; | ||
67 | dump->u_debugreg[4] = 0; | ||
68 | dump->u_debugreg[5] = 0; | ||
69 | dump->u_debugreg[6] = current->thread.debugreg6; | ||
70 | dump->u_debugreg[7] = current->thread.debugreg7; | ||
71 | |||
72 | if (dump->start_stack < 0xc0000000) | ||
73 | dump->u_ssize = ((unsigned long) (0xc0000000 - dump->start_stack)) >> PAGE_SHIFT; | ||
74 | |||
75 | dump->regs.ebx = regs->rbx; | ||
76 | dump->regs.ecx = regs->rcx; | ||
77 | dump->regs.edx = regs->rdx; | ||
78 | dump->regs.esi = regs->rsi; | ||
79 | dump->regs.edi = regs->rdi; | ||
80 | dump->regs.ebp = regs->rbp; | ||
81 | dump->regs.eax = regs->rax; | ||
82 | dump->regs.ds = current->thread.ds; | ||
83 | dump->regs.es = current->thread.es; | ||
84 | asm("movl %%fs,%0" : "=r" (fs)); dump->regs.fs = fs; | ||
85 | asm("movl %%gs,%0" : "=r" (gs)); dump->regs.gs = gs; | ||
86 | dump->regs.orig_eax = regs->orig_rax; | ||
87 | dump->regs.eip = regs->rip; | ||
88 | dump->regs.cs = regs->cs; | ||
89 | dump->regs.eflags = regs->eflags; | ||
90 | dump->regs.esp = regs->rsp; | ||
91 | dump->regs.ss = regs->ss; | ||
92 | |||
93 | #if 1 /* FIXME */ | ||
94 | dump->u_fpvalid = 0; | ||
95 | #else | ||
96 | dump->u_fpvalid = dump_fpu (regs, &dump->i387); | ||
97 | #endif | ||
98 | } | ||
99 | |||
100 | #endif | ||
101 | |||
102 | static struct linux_binfmt aout_format = { | ||
103 | .module = THIS_MODULE, | ||
104 | .load_binary = load_aout_binary, | ||
105 | .load_shlib = load_aout_library, | ||
106 | #if CORE_DUMP | ||
107 | .core_dump = aout_core_dump, | ||
108 | #endif | ||
109 | .min_coredump = PAGE_SIZE | ||
110 | }; | ||
111 | |||
112 | static void set_brk(unsigned long start, unsigned long end) | ||
113 | { | ||
114 | start = PAGE_ALIGN(start); | ||
115 | end = PAGE_ALIGN(end); | ||
116 | if (end <= start) | ||
117 | return; | ||
118 | down_write(¤t->mm->mmap_sem); | ||
119 | do_brk(start, end - start); | ||
120 | up_write(¤t->mm->mmap_sem); | ||
121 | } | ||
122 | |||
123 | #if CORE_DUMP | ||
124 | /* | ||
125 | * These are the only things you should do on a core-file: use only these | ||
126 | * macros to write out all the necessary info. | ||
127 | */ | ||
128 | |||
129 | static int dump_write(struct file *file, const void *addr, int nr) | ||
130 | { | ||
131 | return file->f_op->write(file, addr, nr, &file->f_pos) == nr; | ||
132 | } | ||
133 | |||
134 | #define DUMP_WRITE(addr, nr) \ | ||
135 | if (!dump_write(file, (void *)(addr), (nr))) \ | ||
136 | goto end_coredump; | ||
137 | |||
138 | #define DUMP_SEEK(offset) \ | ||
139 | if (file->f_op->llseek) { \ | ||
140 | if (file->f_op->llseek(file,(offset),0) != (offset)) \ | ||
141 | goto end_coredump; \ | ||
142 | } else file->f_pos = (offset) | ||
143 | |||
144 | /* | ||
145 | * Routine writes a core dump image in the current directory. | ||
146 | * Currently only a stub-function. | ||
147 | * | ||
148 | * Note that setuid/setgid files won't make a core-dump if the uid/gid | ||
149 | * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable" | ||
150 | * field, which also makes sure the core-dumps won't be recursive if the | ||
151 | * dumping of the process results in another error.. | ||
152 | */ | ||
153 | |||
154 | static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file) | ||
155 | { | ||
156 | mm_segment_t fs; | ||
157 | int has_dumped = 0; | ||
158 | unsigned long dump_start, dump_size; | ||
159 | struct user32 dump; | ||
160 | # define START_DATA(u) (u.u_tsize << PAGE_SHIFT) | ||
161 | # define START_STACK(u) (u.start_stack) | ||
162 | |||
163 | fs = get_fs(); | ||
164 | set_fs(KERNEL_DS); | ||
165 | has_dumped = 1; | ||
166 | current->flags |= PF_DUMPCORE; | ||
167 | strncpy(dump.u_comm, current->comm, sizeof(current->comm)); | ||
168 | dump.u_ar0 = (u32)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump))); | ||
169 | dump.signal = signr; | ||
170 | dump_thread32(regs, &dump); | ||
171 | |||
172 | /* If the size of the dump file exceeds the rlimit, then see what would happen | ||
173 | if we wrote the stack, but not the data area. */ | ||
174 | if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE > | ||
175 | current->signal->rlim[RLIMIT_CORE].rlim_cur) | ||
176 | dump.u_dsize = 0; | ||
177 | |||
178 | /* Make sure we have enough room to write the stack and data areas. */ | ||
179 | if ((dump.u_ssize+1) * PAGE_SIZE > | ||
180 | current->signal->rlim[RLIMIT_CORE].rlim_cur) | ||
181 | dump.u_ssize = 0; | ||
182 | |||
183 | /* make sure we actually have a data and stack area to dump */ | ||
184 | set_fs(USER_DS); | ||
185 | if (!access_ok(VERIFY_READ, (void *) (unsigned long)START_DATA(dump), dump.u_dsize << PAGE_SHIFT)) | ||
186 | dump.u_dsize = 0; | ||
187 | if (!access_ok(VERIFY_READ, (void *) (unsigned long)START_STACK(dump), dump.u_ssize << PAGE_SHIFT)) | ||
188 | dump.u_ssize = 0; | ||
189 | |||
190 | set_fs(KERNEL_DS); | ||
191 | /* struct user */ | ||
192 | DUMP_WRITE(&dump,sizeof(dump)); | ||
193 | /* Now dump all of the user data. Include malloced stuff as well */ | ||
194 | DUMP_SEEK(PAGE_SIZE); | ||
195 | /* now we start writing out the user space info */ | ||
196 | set_fs(USER_DS); | ||
197 | /* Dump the data area */ | ||
198 | if (dump.u_dsize != 0) { | ||
199 | dump_start = START_DATA(dump); | ||
200 | dump_size = dump.u_dsize << PAGE_SHIFT; | ||
201 | DUMP_WRITE(dump_start,dump_size); | ||
202 | } | ||
203 | /* Now prepare to dump the stack area */ | ||
204 | if (dump.u_ssize != 0) { | ||
205 | dump_start = START_STACK(dump); | ||
206 | dump_size = dump.u_ssize << PAGE_SHIFT; | ||
207 | DUMP_WRITE(dump_start,dump_size); | ||
208 | } | ||
209 | /* Finally dump the task struct. Not be used by gdb, but could be useful */ | ||
210 | set_fs(KERNEL_DS); | ||
211 | DUMP_WRITE(current,sizeof(*current)); | ||
212 | end_coredump: | ||
213 | set_fs(fs); | ||
214 | return has_dumped; | ||
215 | } | ||
216 | #endif | ||
217 | |||
218 | /* | ||
219 | * create_aout_tables() parses the env- and arg-strings in new user | ||
220 | * memory and creates the pointer tables from them, and puts their | ||
221 | * addresses on the "stack", returning the new stack pointer value. | ||
222 | */ | ||
223 | static u32 __user *create_aout_tables(char __user *p, struct linux_binprm *bprm) | ||
224 | { | ||
225 | u32 __user *argv; | ||
226 | u32 __user *envp; | ||
227 | u32 __user *sp; | ||
228 | int argc = bprm->argc; | ||
229 | int envc = bprm->envc; | ||
230 | |||
231 | sp = (u32 __user *) ((-(unsigned long)sizeof(u32)) & (unsigned long) p); | ||
232 | sp -= envc+1; | ||
233 | envp = sp; | ||
234 | sp -= argc+1; | ||
235 | argv = sp; | ||
236 | put_user((unsigned long) envp,--sp); | ||
237 | put_user((unsigned long) argv,--sp); | ||
238 | put_user(argc,--sp); | ||
239 | current->mm->arg_start = (unsigned long) p; | ||
240 | while (argc-->0) { | ||
241 | char c; | ||
242 | put_user((u32)(unsigned long)p,argv++); | ||
243 | do { | ||
244 | get_user(c,p++); | ||
245 | } while (c); | ||
246 | } | ||
247 | put_user(NULL,argv); | ||
248 | current->mm->arg_end = current->mm->env_start = (unsigned long) p; | ||
249 | while (envc-->0) { | ||
250 | char c; | ||
251 | put_user((u32)(unsigned long)p,envp++); | ||
252 | do { | ||
253 | get_user(c,p++); | ||
254 | } while (c); | ||
255 | } | ||
256 | put_user(NULL,envp); | ||
257 | current->mm->env_end = (unsigned long) p; | ||
258 | return sp; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * These are the functions used to load a.out style executables and shared | ||
263 | * libraries. There is no binary dependent code anywhere else. | ||
264 | */ | ||
265 | |||
266 | static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) | ||
267 | { | ||
268 | struct exec ex; | ||
269 | unsigned long error; | ||
270 | unsigned long fd_offset; | ||
271 | unsigned long rlim; | ||
272 | int retval; | ||
273 | |||
274 | ex = *((struct exec *) bprm->buf); /* exec-header */ | ||
275 | if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && | ||
276 | N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || | ||
277 | N_TRSIZE(ex) || N_DRSIZE(ex) || | ||
278 | i_size_read(bprm->file->f_dentry->d_inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { | ||
279 | return -ENOEXEC; | ||
280 | } | ||
281 | |||
282 | fd_offset = N_TXTOFF(ex); | ||
283 | |||
284 | /* Check initial limits. This avoids letting people circumvent | ||
285 | * size limits imposed on them by creating programs with large | ||
286 | * arrays in the data or bss. | ||
287 | */ | ||
288 | rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur; | ||
289 | if (rlim >= RLIM_INFINITY) | ||
290 | rlim = ~0; | ||
291 | if (ex.a_data + ex.a_bss > rlim) | ||
292 | return -ENOMEM; | ||
293 | |||
294 | /* Flush all traces of the currently running executable */ | ||
295 | retval = flush_old_exec(bprm); | ||
296 | if (retval) | ||
297 | return retval; | ||
298 | |||
299 | regs->cs = __USER32_CS; | ||
300 | regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = | ||
301 | regs->r13 = regs->r14 = regs->r15 = 0; | ||
302 | |||
303 | /* OK, This is the point of no return */ | ||
304 | set_personality(PER_LINUX); | ||
305 | set_thread_flag(TIF_IA32); | ||
306 | clear_thread_flag(TIF_ABI_PENDING); | ||
307 | |||
308 | current->mm->end_code = ex.a_text + | ||
309 | (current->mm->start_code = N_TXTADDR(ex)); | ||
310 | current->mm->end_data = ex.a_data + | ||
311 | (current->mm->start_data = N_DATADDR(ex)); | ||
312 | current->mm->brk = ex.a_bss + | ||
313 | (current->mm->start_brk = N_BSSADDR(ex)); | ||
314 | current->mm->free_area_cache = TASK_UNMAPPED_BASE; | ||
315 | |||
316 | set_mm_counter(current->mm, rss, 0); | ||
317 | current->mm->mmap = NULL; | ||
318 | compute_creds(bprm); | ||
319 | current->flags &= ~PF_FORKNOEXEC; | ||
320 | |||
321 | if (N_MAGIC(ex) == OMAGIC) { | ||
322 | unsigned long text_addr, map_size; | ||
323 | loff_t pos; | ||
324 | |||
325 | text_addr = N_TXTADDR(ex); | ||
326 | |||
327 | pos = 32; | ||
328 | map_size = ex.a_text+ex.a_data; | ||
329 | |||
330 | down_write(¤t->mm->mmap_sem); | ||
331 | error = do_brk(text_addr & PAGE_MASK, map_size); | ||
332 | up_write(¤t->mm->mmap_sem); | ||
333 | |||
334 | if (error != (text_addr & PAGE_MASK)) { | ||
335 | send_sig(SIGKILL, current, 0); | ||
336 | return error; | ||
337 | } | ||
338 | |||
339 | error = bprm->file->f_op->read(bprm->file, (char *)text_addr, | ||
340 | ex.a_text+ex.a_data, &pos); | ||
341 | if ((signed long)error < 0) { | ||
342 | send_sig(SIGKILL, current, 0); | ||
343 | return error; | ||
344 | } | ||
345 | |||
346 | flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data); | ||
347 | } else { | ||
348 | #ifdef WARN_OLD | ||
349 | static unsigned long error_time, error_time2; | ||
350 | if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && | ||
351 | (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ) | ||
352 | { | ||
353 | printk(KERN_NOTICE "executable not page aligned\n"); | ||
354 | error_time2 = jiffies; | ||
355 | } | ||
356 | |||
357 | if ((fd_offset & ~PAGE_MASK) != 0 && | ||
358 | (jiffies-error_time) > 5*HZ) | ||
359 | { | ||
360 | printk(KERN_WARNING | ||
361 | "fd_offset is not page aligned. Please convert program: %s\n", | ||
362 | bprm->file->f_dentry->d_name.name); | ||
363 | error_time = jiffies; | ||
364 | } | ||
365 | #endif | ||
366 | |||
367 | if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) { | ||
368 | loff_t pos = fd_offset; | ||
369 | down_write(¤t->mm->mmap_sem); | ||
370 | do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); | ||
371 | up_write(¤t->mm->mmap_sem); | ||
372 | bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex), | ||
373 | ex.a_text+ex.a_data, &pos); | ||
374 | flush_icache_range((unsigned long) N_TXTADDR(ex), | ||
375 | (unsigned long) N_TXTADDR(ex) + | ||
376 | ex.a_text+ex.a_data); | ||
377 | goto beyond_if; | ||
378 | } | ||
379 | |||
380 | down_write(¤t->mm->mmap_sem); | ||
381 | error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text, | ||
382 | PROT_READ | PROT_EXEC, | ||
383 | MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE | MAP_32BIT, | ||
384 | fd_offset); | ||
385 | up_write(¤t->mm->mmap_sem); | ||
386 | |||
387 | if (error != N_TXTADDR(ex)) { | ||
388 | send_sig(SIGKILL, current, 0); | ||
389 | return error; | ||
390 | } | ||
391 | |||
392 | down_write(¤t->mm->mmap_sem); | ||
393 | error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data, | ||
394 | PROT_READ | PROT_WRITE | PROT_EXEC, | ||
395 | MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE | MAP_32BIT, | ||
396 | fd_offset + ex.a_text); | ||
397 | up_write(¤t->mm->mmap_sem); | ||
398 | if (error != N_DATADDR(ex)) { | ||
399 | send_sig(SIGKILL, current, 0); | ||
400 | return error; | ||
401 | } | ||
402 | } | ||
403 | beyond_if: | ||
404 | set_binfmt(&aout_format); | ||
405 | |||
406 | set_brk(current->mm->start_brk, current->mm->brk); | ||
407 | |||
408 | retval = ia32_setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT); | ||
409 | if (retval < 0) { | ||
410 | /* Someone check-me: is this error path enough? */ | ||
411 | send_sig(SIGKILL, current, 0); | ||
412 | return retval; | ||
413 | } | ||
414 | |||
415 | current->mm->start_stack = | ||
416 | (unsigned long)create_aout_tables((char __user *)bprm->p, bprm); | ||
417 | /* start thread */ | ||
418 | asm volatile("movl %0,%%fs" :: "r" (0)); \ | ||
419 | asm volatile("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); | ||
420 | load_gs_index(0); | ||
421 | (regs)->rip = ex.a_entry; | ||
422 | (regs)->rsp = current->mm->start_stack; | ||
423 | (regs)->eflags = 0x200; | ||
424 | (regs)->cs = __USER32_CS; | ||
425 | (regs)->ss = __USER32_DS; | ||
426 | set_fs(USER_DS); | ||
427 | if (unlikely(current->ptrace & PT_PTRACED)) { | ||
428 | if (current->ptrace & PT_TRACE_EXEC) | ||
429 | ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); | ||
430 | else | ||
431 | send_sig(SIGTRAP, current, 0); | ||
432 | } | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static int load_aout_library(struct file *file) | ||
437 | { | ||
438 | struct inode * inode; | ||
439 | unsigned long bss, start_addr, len; | ||
440 | unsigned long error; | ||
441 | int retval; | ||
442 | struct exec ex; | ||
443 | |||
444 | inode = file->f_dentry->d_inode; | ||
445 | |||
446 | retval = -ENOEXEC; | ||
447 | error = kernel_read(file, 0, (char *) &ex, sizeof(ex)); | ||
448 | if (error != sizeof(ex)) | ||
449 | goto out; | ||
450 | |||
451 | /* We come in here for the regular a.out style of shared libraries */ | ||
452 | if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || | ||
453 | N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || | ||
454 | i_size_read(inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { | ||
455 | goto out; | ||
456 | } | ||
457 | |||
458 | if (N_FLAGS(ex)) | ||
459 | goto out; | ||
460 | |||
461 | /* For QMAGIC, the starting address is 0x20 into the page. We mask | ||
462 | this off to get the starting address for the page */ | ||
463 | |||
464 | start_addr = ex.a_entry & 0xfffff000; | ||
465 | |||
466 | if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { | ||
467 | loff_t pos = N_TXTOFF(ex); | ||
468 | |||
469 | #ifdef WARN_OLD | ||
470 | static unsigned long error_time; | ||
471 | if ((jiffies-error_time) > 5*HZ) | ||
472 | { | ||
473 | printk(KERN_WARNING | ||
474 | "N_TXTOFF is not page aligned. Please convert library: %s\n", | ||
475 | file->f_dentry->d_name.name); | ||
476 | error_time = jiffies; | ||
477 | } | ||
478 | #endif | ||
479 | down_write(¤t->mm->mmap_sem); | ||
480 | do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); | ||
481 | up_write(¤t->mm->mmap_sem); | ||
482 | |||
483 | file->f_op->read(file, (char *)start_addr, | ||
484 | ex.a_text + ex.a_data, &pos); | ||
485 | flush_icache_range((unsigned long) start_addr, | ||
486 | (unsigned long) start_addr + ex.a_text + ex.a_data); | ||
487 | |||
488 | retval = 0; | ||
489 | goto out; | ||
490 | } | ||
491 | /* Now use mmap to map the library into memory. */ | ||
492 | down_write(¤t->mm->mmap_sem); | ||
493 | error = do_mmap(file, start_addr, ex.a_text + ex.a_data, | ||
494 | PROT_READ | PROT_WRITE | PROT_EXEC, | ||
495 | MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_32BIT, | ||
496 | N_TXTOFF(ex)); | ||
497 | up_write(¤t->mm->mmap_sem); | ||
498 | retval = error; | ||
499 | if (error != start_addr) | ||
500 | goto out; | ||
501 | |||
502 | len = PAGE_ALIGN(ex.a_text + ex.a_data); | ||
503 | bss = ex.a_text + ex.a_data + ex.a_bss; | ||
504 | if (bss > len) { | ||
505 | down_write(¤t->mm->mmap_sem); | ||
506 | error = do_brk(start_addr + len, bss - len); | ||
507 | up_write(¤t->mm->mmap_sem); | ||
508 | retval = error; | ||
509 | if (error != start_addr + len) | ||
510 | goto out; | ||
511 | } | ||
512 | retval = 0; | ||
513 | out: | ||
514 | return retval; | ||
515 | } | ||
516 | |||
517 | static int __init init_aout_binfmt(void) | ||
518 | { | ||
519 | return register_binfmt(&aout_format); | ||
520 | } | ||
521 | |||
522 | static void __exit exit_aout_binfmt(void) | ||
523 | { | ||
524 | unregister_binfmt(&aout_format); | ||
525 | } | ||
526 | |||
527 | module_init(init_aout_binfmt); | ||
528 | module_exit(exit_aout_binfmt); | ||
529 | MODULE_LICENSE("GPL"); | ||
diff --git a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c new file mode 100644 index 000000000000..93d568dfa762 --- /dev/null +++ b/arch/x86_64/ia32/ia32_binfmt.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * Written 2000,2002 by Andi Kleen. | ||
3 | * | ||
4 | * Loosely based on the sparc64 and IA64 32bit emulation loaders. | ||
5 | * This tricks binfmt_elf.c into loading 32bit binaries using lots | ||
6 | * of ugly preprocessor tricks. Talk about very very poor man's inheritance. | ||
7 | */ | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/config.h> | ||
10 | #include <linux/stddef.h> | ||
11 | #include <linux/rwsem.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/compat.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/binfmts.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/security.h> | ||
18 | |||
19 | #include <asm/segment.h> | ||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/processor.h> | ||
22 | #include <asm/user32.h> | ||
23 | #include <asm/sigcontext32.h> | ||
24 | #include <asm/fpu32.h> | ||
25 | #include <asm/i387.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/ia32.h> | ||
28 | #include <asm/vsyscall32.h> | ||
29 | |||
30 | #define ELF_NAME "elf/i386" | ||
31 | |||
32 | #define AT_SYSINFO 32 | ||
33 | #define AT_SYSINFO_EHDR 33 | ||
34 | |||
35 | int sysctl_vsyscall32 = 1; | ||
36 | |||
37 | #define ARCH_DLINFO do { \ | ||
38 | if (sysctl_vsyscall32) { \ | ||
39 | NEW_AUX_ENT(AT_SYSINFO, (u32)(u64)VSYSCALL32_VSYSCALL); \ | ||
40 | NEW_AUX_ENT(AT_SYSINFO_EHDR, VSYSCALL32_BASE); \ | ||
41 | } \ | ||
42 | } while(0) | ||
43 | |||
44 | struct file; | ||
45 | struct elf_phdr; | ||
46 | |||
47 | #define IA32_EMULATOR 1 | ||
48 | |||
49 | #define ELF_ET_DYN_BASE (TASK_UNMAPPED_32 + 0x1000000) | ||
50 | |||
51 | #undef ELF_ARCH | ||
52 | #define ELF_ARCH EM_386 | ||
53 | |||
54 | #undef ELF_CLASS | ||
55 | #define ELF_CLASS ELFCLASS32 | ||
56 | |||
57 | #define ELF_DATA ELFDATA2LSB | ||
58 | |||
59 | #define USE_ELF_CORE_DUMP 1 | ||
60 | |||
61 | /* Overwrite elfcore.h */ | ||
62 | #define _LINUX_ELFCORE_H 1 | ||
63 | typedef unsigned int elf_greg_t; | ||
64 | |||
65 | #define ELF_NGREG (sizeof (struct user_regs_struct32) / sizeof(elf_greg_t)) | ||
66 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | ||
67 | |||
68 | /* | ||
69 | * These macros parameterize elf_core_dump in fs/binfmt_elf.c to write out | ||
70 | * extra segments containing the vsyscall DSO contents. Dumping its | ||
71 | * contents makes post-mortem fully interpretable later without matching up | ||
72 | * the same kernel and hardware config to see what PC values meant. | ||
73 | * Dumping its extra ELF program headers includes all the other information | ||
74 | * a debugger needs to easily find how the vsyscall DSO was being used. | ||
75 | */ | ||
76 | #define ELF_CORE_EXTRA_PHDRS (VSYSCALL32_EHDR->e_phnum) | ||
77 | #define ELF_CORE_WRITE_EXTRA_PHDRS \ | ||
78 | do { \ | ||
79 | const struct elf32_phdr *const vsyscall_phdrs = \ | ||
80 | (const struct elf32_phdr *) (VSYSCALL32_BASE \ | ||
81 | + VSYSCALL32_EHDR->e_phoff); \ | ||
82 | int i; \ | ||
83 | Elf32_Off ofs = 0; \ | ||
84 | for (i = 0; i < VSYSCALL32_EHDR->e_phnum; ++i) { \ | ||
85 | struct elf32_phdr phdr = vsyscall_phdrs[i]; \ | ||
86 | if (phdr.p_type == PT_LOAD) { \ | ||
87 | BUG_ON(ofs != 0); \ | ||
88 | ofs = phdr.p_offset = offset; \ | ||
89 | phdr.p_memsz = PAGE_ALIGN(phdr.p_memsz); \ | ||
90 | phdr.p_filesz = phdr.p_memsz; \ | ||
91 | offset += phdr.p_filesz; \ | ||
92 | } \ | ||
93 | else \ | ||
94 | phdr.p_offset += ofs; \ | ||
95 | phdr.p_paddr = 0; /* match other core phdrs */ \ | ||
96 | DUMP_WRITE(&phdr, sizeof(phdr)); \ | ||
97 | } \ | ||
98 | } while (0) | ||
99 | #define ELF_CORE_WRITE_EXTRA_DATA \ | ||
100 | do { \ | ||
101 | const struct elf32_phdr *const vsyscall_phdrs = \ | ||
102 | (const struct elf32_phdr *) (VSYSCALL32_BASE \ | ||
103 | + VSYSCALL32_EHDR->e_phoff); \ | ||
104 | int i; \ | ||
105 | for (i = 0; i < VSYSCALL32_EHDR->e_phnum; ++i) { \ | ||
106 | if (vsyscall_phdrs[i].p_type == PT_LOAD) \ | ||
107 | DUMP_WRITE((void *) (u64) vsyscall_phdrs[i].p_vaddr, \ | ||
108 | PAGE_ALIGN(vsyscall_phdrs[i].p_memsz)); \ | ||
109 | } \ | ||
110 | } while (0) | ||
111 | |||
112 | struct elf_siginfo | ||
113 | { | ||
114 | int si_signo; /* signal number */ | ||
115 | int si_code; /* extra code */ | ||
116 | int si_errno; /* errno */ | ||
117 | }; | ||
118 | |||
119 | #define jiffies_to_timeval(a,b) do { (b)->tv_usec = 0; (b)->tv_sec = (a)/HZ; }while(0) | ||
120 | |||
121 | struct elf_prstatus | ||
122 | { | ||
123 | struct elf_siginfo pr_info; /* Info associated with signal */ | ||
124 | short pr_cursig; /* Current signal */ | ||
125 | unsigned int pr_sigpend; /* Set of pending signals */ | ||
126 | unsigned int pr_sighold; /* Set of held signals */ | ||
127 | pid_t pr_pid; | ||
128 | pid_t pr_ppid; | ||
129 | pid_t pr_pgrp; | ||
130 | pid_t pr_sid; | ||
131 | struct compat_timeval pr_utime; /* User time */ | ||
132 | struct compat_timeval pr_stime; /* System time */ | ||
133 | struct compat_timeval pr_cutime; /* Cumulative user time */ | ||
134 | struct compat_timeval pr_cstime; /* Cumulative system time */ | ||
135 | elf_gregset_t pr_reg; /* GP registers */ | ||
136 | int pr_fpvalid; /* True if math co-processor being used. */ | ||
137 | }; | ||
138 | |||
139 | #define ELF_PRARGSZ (80) /* Number of chars for args */ | ||
140 | |||
141 | struct elf_prpsinfo | ||
142 | { | ||
143 | char pr_state; /* numeric process state */ | ||
144 | char pr_sname; /* char for pr_state */ | ||
145 | char pr_zomb; /* zombie */ | ||
146 | char pr_nice; /* nice val */ | ||
147 | unsigned int pr_flag; /* flags */ | ||
148 | __u16 pr_uid; | ||
149 | __u16 pr_gid; | ||
150 | pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; | ||
151 | /* Lots missing */ | ||
152 | char pr_fname[16]; /* filename of executable */ | ||
153 | char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ | ||
154 | }; | ||
155 | |||
156 | #define __STR(x) #x | ||
157 | #define STR(x) __STR(x) | ||
158 | |||
159 | #define _GET_SEG(x) \ | ||
160 | ({ __u32 seg; asm("movl %%" STR(x) ",%0" : "=r"(seg)); seg; }) | ||
161 | |||
162 | /* Assumes current==process to be dumped */ | ||
163 | #define ELF_CORE_COPY_REGS(pr_reg, regs) \ | ||
164 | pr_reg[0] = regs->rbx; \ | ||
165 | pr_reg[1] = regs->rcx; \ | ||
166 | pr_reg[2] = regs->rdx; \ | ||
167 | pr_reg[3] = regs->rsi; \ | ||
168 | pr_reg[4] = regs->rdi; \ | ||
169 | pr_reg[5] = regs->rbp; \ | ||
170 | pr_reg[6] = regs->rax; \ | ||
171 | pr_reg[7] = _GET_SEG(ds); \ | ||
172 | pr_reg[8] = _GET_SEG(es); \ | ||
173 | pr_reg[9] = _GET_SEG(fs); \ | ||
174 | pr_reg[10] = _GET_SEG(gs); \ | ||
175 | pr_reg[11] = regs->orig_rax; \ | ||
176 | pr_reg[12] = regs->rip; \ | ||
177 | pr_reg[13] = regs->cs; \ | ||
178 | pr_reg[14] = regs->eflags; \ | ||
179 | pr_reg[15] = regs->rsp; \ | ||
180 | pr_reg[16] = regs->ss; | ||
181 | |||
182 | #define user user32 | ||
183 | |||
184 | #define __ASM_X86_64_ELF_H 1 | ||
185 | #define elf_read_implies_exec(ex, have_pt_gnu_stack) (!(have_pt_gnu_stack)) | ||
186 | //#include <asm/ia32.h> | ||
187 | #include <linux/elf.h> | ||
188 | |||
189 | typedef struct user_i387_ia32_struct elf_fpregset_t; | ||
190 | typedef struct user32_fxsr_struct elf_fpxregset_t; | ||
191 | |||
192 | |||
193 | static inline void elf_core_copy_regs(elf_gregset_t *elfregs, struct pt_regs *regs) | ||
194 | { | ||
195 | ELF_CORE_COPY_REGS((*elfregs), regs) | ||
196 | } | ||
197 | |||
198 | static inline int elf_core_copy_task_regs(struct task_struct *t, elf_gregset_t* elfregs) | ||
199 | { | ||
200 | struct pt_regs *pp = (struct pt_regs *)(t->thread.rsp0); | ||
201 | --pp; | ||
202 | ELF_CORE_COPY_REGS((*elfregs), pp); | ||
203 | /* fix wrong segments */ | ||
204 | (*elfregs)[7] = t->thread.ds; | ||
205 | (*elfregs)[9] = t->thread.fsindex; | ||
206 | (*elfregs)[10] = t->thread.gsindex; | ||
207 | (*elfregs)[8] = t->thread.es; | ||
208 | return 1; | ||
209 | } | ||
210 | |||
211 | static inline int | ||
212 | elf_core_copy_task_fpregs(struct task_struct *tsk, struct pt_regs *regs, elf_fpregset_t *fpu) | ||
213 | { | ||
214 | struct _fpstate_ia32 *fpstate = (void*)fpu; | ||
215 | mm_segment_t oldfs = get_fs(); | ||
216 | |||
217 | if (!tsk_used_math(tsk)) | ||
218 | return 0; | ||
219 | if (!regs) | ||
220 | regs = (struct pt_regs *)tsk->thread.rsp0; | ||
221 | --regs; | ||
222 | if (tsk == current) | ||
223 | unlazy_fpu(tsk); | ||
224 | set_fs(KERNEL_DS); | ||
225 | save_i387_ia32(tsk, fpstate, regs, 1); | ||
226 | /* Correct for i386 bug. It puts the fop into the upper 16bits of | ||
227 | the tag word (like FXSAVE), not into the fcs*/ | ||
228 | fpstate->cssel |= fpstate->tag & 0xffff0000; | ||
229 | set_fs(oldfs); | ||
230 | return 1; | ||
231 | } | ||
232 | |||
233 | #define ELF_CORE_COPY_XFPREGS 1 | ||
234 | static inline int | ||
235 | elf_core_copy_task_xfpregs(struct task_struct *t, elf_fpxregset_t *xfpu) | ||
236 | { | ||
237 | struct pt_regs *regs = ((struct pt_regs *)(t->thread.rsp0))-1; | ||
238 | if (!tsk_used_math(t)) | ||
239 | return 0; | ||
240 | if (t == current) | ||
241 | unlazy_fpu(t); | ||
242 | memcpy(xfpu, &t->thread.i387.fxsave, sizeof(elf_fpxregset_t)); | ||
243 | xfpu->fcs = regs->cs; | ||
244 | xfpu->fos = t->thread.ds; /* right? */ | ||
245 | return 1; | ||
246 | } | ||
247 | |||
248 | #undef elf_check_arch | ||
249 | #define elf_check_arch(x) \ | ||
250 | ((x)->e_machine == EM_386) | ||
251 | |||
252 | extern int force_personality32; | ||
253 | |||
254 | #define ELF_EXEC_PAGESIZE PAGE_SIZE | ||
255 | #define ELF_HWCAP (boot_cpu_data.x86_capability[0]) | ||
256 | #define ELF_PLATFORM ("i686") | ||
257 | #define SET_PERSONALITY(ex, ibcs2) \ | ||
258 | do { \ | ||
259 | unsigned long new_flags = 0; \ | ||
260 | if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \ | ||
261 | new_flags = _TIF_IA32; \ | ||
262 | if ((current_thread_info()->flags & _TIF_IA32) \ | ||
263 | != new_flags) \ | ||
264 | set_thread_flag(TIF_ABI_PENDING); \ | ||
265 | else \ | ||
266 | clear_thread_flag(TIF_ABI_PENDING); \ | ||
267 | /* XXX This overwrites the user set personality */ \ | ||
268 | current->personality |= force_personality32; \ | ||
269 | } while (0) | ||
270 | |||
271 | /* Override some function names */ | ||
272 | #define elf_format elf32_format | ||
273 | |||
274 | #define init_elf_binfmt init_elf32_binfmt | ||
275 | #define exit_elf_binfmt exit_elf32_binfmt | ||
276 | |||
277 | #define load_elf_binary load_elf32_binary | ||
278 | |||
279 | #define ELF_PLAT_INIT(r, load_addr) elf32_init(r) | ||
280 | #define setup_arg_pages(bprm, stack_top, exec_stack) \ | ||
281 | ia32_setup_arg_pages(bprm, stack_top, exec_stack) | ||
282 | int ia32_setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, int executable_stack); | ||
283 | |||
284 | #undef start_thread | ||
285 | #define start_thread(regs,new_rip,new_rsp) do { \ | ||
286 | asm volatile("movl %0,%%fs" :: "r" (0)); \ | ||
287 | asm volatile("movl %0,%%es; movl %0,%%ds": :"r" (__USER32_DS)); \ | ||
288 | load_gs_index(0); \ | ||
289 | (regs)->rip = (new_rip); \ | ||
290 | (regs)->rsp = (new_rsp); \ | ||
291 | (regs)->eflags = 0x200; \ | ||
292 | (regs)->cs = __USER32_CS; \ | ||
293 | (regs)->ss = __USER32_DS; \ | ||
294 | set_fs(USER_DS); \ | ||
295 | } while(0) | ||
296 | |||
297 | |||
298 | #define elf_map elf32_map | ||
299 | |||
300 | #include <linux/module.h> | ||
301 | |||
302 | MODULE_DESCRIPTION("Binary format loader for compatibility with IA32 ELF binaries."); | ||
303 | MODULE_AUTHOR("Eric Youngdale, Andi Kleen"); | ||
304 | |||
305 | #undef MODULE_DESCRIPTION | ||
306 | #undef MODULE_AUTHOR | ||
307 | |||
308 | #define elf_addr_t __u32 | ||
309 | |||
310 | #undef TASK_SIZE | ||
311 | #define TASK_SIZE 0xffffffff | ||
312 | |||
313 | static void elf32_init(struct pt_regs *); | ||
314 | |||
315 | #include "../../../fs/binfmt_elf.c" | ||
316 | |||
317 | static void elf32_init(struct pt_regs *regs) | ||
318 | { | ||
319 | struct task_struct *me = current; | ||
320 | regs->rdi = 0; | ||
321 | regs->rsi = 0; | ||
322 | regs->rdx = 0; | ||
323 | regs->rcx = 0; | ||
324 | regs->rax = 0; | ||
325 | regs->rbx = 0; | ||
326 | regs->rbp = 0; | ||
327 | regs->r8 = regs->r9 = regs->r10 = regs->r11 = regs->r12 = | ||
328 | regs->r13 = regs->r14 = regs->r15 = 0; | ||
329 | me->thread.fs = 0; | ||
330 | me->thread.gs = 0; | ||
331 | me->thread.fsindex = 0; | ||
332 | me->thread.gsindex = 0; | ||
333 | me->thread.ds = __USER_DS; | ||
334 | me->thread.es = __USER_DS; | ||
335 | } | ||
336 | |||
337 | int setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, int executable_stack) | ||
338 | { | ||
339 | unsigned long stack_base; | ||
340 | struct vm_area_struct *mpnt; | ||
341 | struct mm_struct *mm = current->mm; | ||
342 | int i, ret; | ||
343 | |||
344 | stack_base = IA32_STACK_TOP - MAX_ARG_PAGES * PAGE_SIZE; | ||
345 | mm->arg_start = bprm->p + stack_base; | ||
346 | |||
347 | bprm->p += stack_base; | ||
348 | if (bprm->loader) | ||
349 | bprm->loader += stack_base; | ||
350 | bprm->exec += stack_base; | ||
351 | |||
352 | mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); | ||
353 | if (!mpnt) | ||
354 | return -ENOMEM; | ||
355 | |||
356 | if (security_vm_enough_memory((IA32_STACK_TOP - (PAGE_MASK & (unsigned long) bprm->p))>>PAGE_SHIFT)) { | ||
357 | kmem_cache_free(vm_area_cachep, mpnt); | ||
358 | return -ENOMEM; | ||
359 | } | ||
360 | |||
361 | memset(mpnt, 0, sizeof(*mpnt)); | ||
362 | |||
363 | down_write(&mm->mmap_sem); | ||
364 | { | ||
365 | mpnt->vm_mm = mm; | ||
366 | mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; | ||
367 | mpnt->vm_end = IA32_STACK_TOP; | ||
368 | if (executable_stack == EXSTACK_ENABLE_X) | ||
369 | mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC; | ||
370 | else if (executable_stack == EXSTACK_DISABLE_X) | ||
371 | mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC; | ||
372 | else | ||
373 | mpnt->vm_flags = VM_STACK_FLAGS; | ||
374 | mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? | ||
375 | PAGE_COPY_EXEC : PAGE_COPY; | ||
376 | if ((ret = insert_vm_struct(mm, mpnt))) { | ||
377 | up_write(&mm->mmap_sem); | ||
378 | kmem_cache_free(vm_area_cachep, mpnt); | ||
379 | return ret; | ||
380 | } | ||
381 | mm->stack_vm = mm->total_vm = vma_pages(mpnt); | ||
382 | } | ||
383 | |||
384 | for (i = 0 ; i < MAX_ARG_PAGES ; i++) { | ||
385 | struct page *page = bprm->page[i]; | ||
386 | if (page) { | ||
387 | bprm->page[i] = NULL; | ||
388 | install_arg_page(mpnt, page, stack_base); | ||
389 | } | ||
390 | stack_base += PAGE_SIZE; | ||
391 | } | ||
392 | up_write(&mm->mmap_sem); | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static unsigned long | ||
398 | elf32_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type) | ||
399 | { | ||
400 | unsigned long map_addr; | ||
401 | struct task_struct *me = current; | ||
402 | |||
403 | down_write(&me->mm->mmap_sem); | ||
404 | map_addr = do_mmap(filep, ELF_PAGESTART(addr), | ||
405 | eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, | ||
406 | type, | ||
407 | eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); | ||
408 | up_write(&me->mm->mmap_sem); | ||
409 | return(map_addr); | ||
410 | } | ||
411 | |||
412 | #ifdef CONFIG_SYSCTL | ||
413 | /* Register vsyscall32 into the ABI table */ | ||
414 | #include <linux/sysctl.h> | ||
415 | |||
416 | static ctl_table abi_table2[] = { | ||
417 | { 99, "vsyscall32", &sysctl_vsyscall32, sizeof(int), 0644, NULL, | ||
418 | proc_dointvec }, | ||
419 | { 0, } | ||
420 | }; | ||
421 | |||
422 | static ctl_table abi_root_table2[] = { | ||
423 | { .ctl_name = CTL_ABI, .procname = "abi", .mode = 0555, | ||
424 | .child = abi_table2 }, | ||
425 | { 0 }, | ||
426 | }; | ||
427 | |||
428 | static __init int ia32_binfmt_init(void) | ||
429 | { | ||
430 | register_sysctl_table(abi_root_table2, 1); | ||
431 | return 0; | ||
432 | } | ||
433 | __initcall(ia32_binfmt_init); | ||
434 | #endif | ||
diff --git a/arch/x86_64/ia32/ia32_ioctl.c b/arch/x86_64/ia32/ia32_ioctl.c new file mode 100644 index 000000000000..d259f8a6f811 --- /dev/null +++ b/arch/x86_64/ia32/ia32_ioctl.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* $Id: ia32_ioctl.c,v 1.25 2002/10/11 07:17:06 ak Exp $ | ||
2 | * ioctl32.c: Conversion between 32bit and 64bit native ioctls. | ||
3 | * | ||
4 | * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) | ||
5 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) | ||
6 | * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs | ||
7 | * | ||
8 | * These routines maintain argument size conversion between 32bit and 64bit | ||
9 | * ioctls. | ||
10 | */ | ||
11 | |||
12 | #define INCLUDES | ||
13 | #include <linux/syscalls.h> | ||
14 | #include "compat_ioctl.c" | ||
15 | #include <asm/mtrr.h> | ||
16 | #include <asm/ia32.h> | ||
17 | |||
18 | #define CODE | ||
19 | #include "compat_ioctl.c" | ||
20 | |||
21 | #ifndef TIOCGDEV | ||
22 | #define TIOCGDEV _IOR('T',0x32, unsigned int) | ||
23 | #endif | ||
24 | static int tiocgdev(unsigned fd, unsigned cmd, unsigned int __user *ptr) | ||
25 | { | ||
26 | |||
27 | struct file *file = fget(fd); | ||
28 | struct tty_struct *real_tty; | ||
29 | |||
30 | if (!file) | ||
31 | return -EBADF; | ||
32 | if (file->f_op->ioctl != tty_ioctl) | ||
33 | return -EINVAL; | ||
34 | real_tty = (struct tty_struct *)file->private_data; | ||
35 | if (!real_tty) | ||
36 | return -EINVAL; | ||
37 | return put_user(new_encode_dev(tty_devnum(real_tty)), ptr); | ||
38 | } | ||
39 | |||
40 | #define RTC_IRQP_READ32 _IOR('p', 0x0b, unsigned int) /* Read IRQ rate */ | ||
41 | #define RTC_IRQP_SET32 _IOW('p', 0x0c, unsigned int) /* Set IRQ rate */ | ||
42 | #define RTC_EPOCH_READ32 _IOR('p', 0x0d, unsigned) /* Read epoch */ | ||
43 | #define RTC_EPOCH_SET32 _IOW('p', 0x0e, unsigned) /* Set epoch */ | ||
44 | |||
45 | static int rtc32_ioctl(unsigned fd, unsigned cmd, unsigned long arg) | ||
46 | { | ||
47 | unsigned long val; | ||
48 | mm_segment_t oldfs = get_fs(); | ||
49 | int ret; | ||
50 | |||
51 | switch (cmd) { | ||
52 | case RTC_IRQP_READ32: | ||
53 | set_fs(KERNEL_DS); | ||
54 | ret = sys_ioctl(fd, RTC_IRQP_READ, (unsigned long)&val); | ||
55 | set_fs(oldfs); | ||
56 | if (!ret) | ||
57 | ret = put_user(val, (unsigned int __user *) arg); | ||
58 | return ret; | ||
59 | |||
60 | case RTC_IRQP_SET32: | ||
61 | cmd = RTC_IRQP_SET; | ||
62 | break; | ||
63 | |||
64 | case RTC_EPOCH_READ32: | ||
65 | set_fs(KERNEL_DS); | ||
66 | ret = sys_ioctl(fd, RTC_EPOCH_READ, (unsigned long) &val); | ||
67 | set_fs(oldfs); | ||
68 | if (!ret) | ||
69 | ret = put_user(val, (unsigned int __user *) arg); | ||
70 | return ret; | ||
71 | |||
72 | case RTC_EPOCH_SET32: | ||
73 | cmd = RTC_EPOCH_SET; | ||
74 | break; | ||
75 | } | ||
76 | return sys_ioctl(fd,cmd,arg); | ||
77 | } | ||
78 | |||
79 | /* /proc/mtrr ioctls */ | ||
80 | |||
81 | |||
82 | struct mtrr_sentry32 | ||
83 | { | ||
84 | compat_ulong_t base; /* Base address */ | ||
85 | compat_uint_t size; /* Size of region */ | ||
86 | compat_uint_t type; /* Type of region */ | ||
87 | }; | ||
88 | |||
89 | struct mtrr_gentry32 | ||
90 | { | ||
91 | compat_ulong_t regnum; /* Register number */ | ||
92 | compat_uint_t base; /* Base address */ | ||
93 | compat_uint_t size; /* Size of region */ | ||
94 | compat_uint_t type; /* Type of region */ | ||
95 | }; | ||
96 | |||
97 | #define MTRR_IOCTL_BASE 'M' | ||
98 | |||
99 | #define MTRRIOC32_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry32) | ||
100 | #define MTRRIOC32_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry32) | ||
101 | #define MTRRIOC32_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry32) | ||
102 | #define MTRRIOC32_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry32) | ||
103 | #define MTRRIOC32_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry32) | ||
104 | #define MTRRIOC32_ADD_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_sentry32) | ||
105 | #define MTRRIOC32_SET_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 6, struct mtrr_sentry32) | ||
106 | #define MTRRIOC32_DEL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 7, struct mtrr_sentry32) | ||
107 | #define MTRRIOC32_GET_PAGE_ENTRY _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry32) | ||
108 | #define MTRRIOC32_KILL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 9, struct mtrr_sentry32) | ||
109 | |||
110 | |||
111 | static int mtrr_ioctl32(unsigned int fd, unsigned int cmd, unsigned long arg) | ||
112 | { | ||
113 | struct mtrr_gentry g; | ||
114 | struct mtrr_sentry s; | ||
115 | int get = 0, err = 0; | ||
116 | struct mtrr_gentry32 __user *g32 = (struct mtrr_gentry32 __user *)arg; | ||
117 | mm_segment_t oldfs = get_fs(); | ||
118 | |||
119 | switch (cmd) { | ||
120 | #define SET(x) case MTRRIOC32_ ## x ## _ENTRY: cmd = MTRRIOC_ ## x ## _ENTRY; break | ||
121 | #define GET(x) case MTRRIOC32_ ## x ## _ENTRY: cmd = MTRRIOC_ ## x ## _ENTRY; get=1; break | ||
122 | SET(ADD); | ||
123 | SET(SET); | ||
124 | SET(DEL); | ||
125 | GET(GET); | ||
126 | SET(KILL); | ||
127 | SET(ADD_PAGE); | ||
128 | SET(SET_PAGE); | ||
129 | SET(DEL_PAGE); | ||
130 | GET(GET_PAGE); | ||
131 | SET(KILL_PAGE); | ||
132 | } | ||
133 | |||
134 | if (get) { | ||
135 | err = get_user(g.regnum, &g32->regnum); | ||
136 | err |= get_user(g.base, &g32->base); | ||
137 | err |= get_user(g.size, &g32->size); | ||
138 | err |= get_user(g.type, &g32->type); | ||
139 | |||
140 | arg = (unsigned long)&g; | ||
141 | } else { | ||
142 | struct mtrr_sentry32 __user *s32 = (struct mtrr_sentry32 __user *)arg; | ||
143 | err = get_user(s.base, &s32->base); | ||
144 | err |= get_user(s.size, &s32->size); | ||
145 | err |= get_user(s.type, &s32->type); | ||
146 | |||
147 | arg = (unsigned long)&s; | ||
148 | } | ||
149 | if (err) return err; | ||
150 | |||
151 | set_fs(KERNEL_DS); | ||
152 | err = sys_ioctl(fd, cmd, arg); | ||
153 | set_fs(oldfs); | ||
154 | |||
155 | if (!err && get) { | ||
156 | err = put_user(g.base, &g32->base); | ||
157 | err |= put_user(g.size, &g32->size); | ||
158 | err |= put_user(g.regnum, &g32->regnum); | ||
159 | err |= put_user(g.type, &g32->type); | ||
160 | } | ||
161 | return err; | ||
162 | } | ||
163 | |||
164 | #define HANDLE_IOCTL(cmd,handler) { (cmd), (ioctl_trans_handler_t)(handler) }, | ||
165 | #define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL(cmd,sys_ioctl) | ||
166 | |||
167 | struct ioctl_trans ioctl_start[] = { | ||
168 | #include <linux/compat_ioctl.h> | ||
169 | #define DECLARES | ||
170 | #include "compat_ioctl.c" | ||
171 | COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS) | ||
172 | COMPATIBLE_IOCTL(HDIO_SCAN_HWIF) | ||
173 | COMPATIBLE_IOCTL(BLKRASET) | ||
174 | COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ | ||
175 | COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ | ||
176 | COMPATIBLE_IOCTL(FIOQSIZE) | ||
177 | |||
178 | /* And these ioctls need translation */ | ||
179 | HANDLE_IOCTL(TIOCGDEV, tiocgdev) | ||
180 | /* realtime device */ | ||
181 | HANDLE_IOCTL(RTC_IRQP_READ, rtc32_ioctl) | ||
182 | HANDLE_IOCTL(RTC_IRQP_READ32,rtc32_ioctl) | ||
183 | HANDLE_IOCTL(RTC_IRQP_SET32, rtc32_ioctl) | ||
184 | HANDLE_IOCTL(RTC_EPOCH_READ32, rtc32_ioctl) | ||
185 | HANDLE_IOCTL(RTC_EPOCH_SET32, rtc32_ioctl) | ||
186 | /* take care of sizeof(sizeof()) breakage */ | ||
187 | /* mtrr */ | ||
188 | HANDLE_IOCTL(MTRRIOC32_ADD_ENTRY, mtrr_ioctl32) | ||
189 | HANDLE_IOCTL(MTRRIOC32_SET_ENTRY, mtrr_ioctl32) | ||
190 | HANDLE_IOCTL(MTRRIOC32_DEL_ENTRY, mtrr_ioctl32) | ||
191 | HANDLE_IOCTL(MTRRIOC32_GET_ENTRY, mtrr_ioctl32) | ||
192 | HANDLE_IOCTL(MTRRIOC32_KILL_ENTRY, mtrr_ioctl32) | ||
193 | HANDLE_IOCTL(MTRRIOC32_ADD_PAGE_ENTRY, mtrr_ioctl32) | ||
194 | HANDLE_IOCTL(MTRRIOC32_SET_PAGE_ENTRY, mtrr_ioctl32) | ||
195 | HANDLE_IOCTL(MTRRIOC32_DEL_PAGE_ENTRY, mtrr_ioctl32) | ||
196 | HANDLE_IOCTL(MTRRIOC32_GET_PAGE_ENTRY, mtrr_ioctl32) | ||
197 | HANDLE_IOCTL(MTRRIOC32_KILL_PAGE_ENTRY, mtrr_ioctl32) | ||
198 | }; | ||
199 | |||
200 | int ioctl_table_size = ARRAY_SIZE(ioctl_start); | ||
201 | |||
diff --git a/arch/x86_64/ia32/ia32_signal.c b/arch/x86_64/ia32/ia32_signal.c new file mode 100644 index 000000000000..fbd09b5126ce --- /dev/null +++ b/arch/x86_64/ia32/ia32_signal.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * linux/arch/x86_64/ia32/ia32_signal.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | * | ||
6 | * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson | ||
7 | * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes | ||
8 | * 2000-12-* x86-64 compatibility mode signal handling by Andi Kleen | ||
9 | * | ||
10 | * $Id: ia32_signal.c,v 1.22 2002/07/29 10:34:03 ak Exp $ | ||
11 | */ | ||
12 | |||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/smp.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/signal.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/wait.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/stddef.h> | ||
24 | #include <linux/personality.h> | ||
25 | #include <linux/compat.h> | ||
26 | #include <asm/ucontext.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <asm/i387.h> | ||
29 | #include <asm/ia32.h> | ||
30 | #include <asm/ptrace.h> | ||
31 | #include <asm/ia32_unistd.h> | ||
32 | #include <asm/user32.h> | ||
33 | #include <asm/sigcontext32.h> | ||
34 | #include <asm/fpu32.h> | ||
35 | #include <asm/proto.h> | ||
36 | #include <asm/vsyscall32.h> | ||
37 | |||
38 | #define DEBUG_SIG 0 | ||
39 | |||
40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
41 | |||
42 | asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); | ||
43 | void signal_fault(struct pt_regs *regs, void __user *frame, char *where); | ||
44 | |||
45 | int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) | ||
46 | { | ||
47 | int err; | ||
48 | if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) | ||
49 | return -EFAULT; | ||
50 | |||
51 | /* If you change siginfo_t structure, please make sure that | ||
52 | this code is fixed accordingly. | ||
53 | It should never copy any pad contained in the structure | ||
54 | to avoid security leaks, but must copy the generic | ||
55 | 3 ints plus the relevant union member. */ | ||
56 | err = __put_user(from->si_signo, &to->si_signo); | ||
57 | err |= __put_user(from->si_errno, &to->si_errno); | ||
58 | err |= __put_user((short)from->si_code, &to->si_code); | ||
59 | |||
60 | if (from->si_code < 0) { | ||
61 | err |= __put_user(from->si_pid, &to->si_pid); | ||
62 | err |= __put_user(from->si_uid, &to->si_uid); | ||
63 | err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr); | ||
64 | } else { | ||
65 | /* First 32bits of unions are always present: | ||
66 | * si_pid === si_band === si_tid === si_addr(LS half) */ | ||
67 | err |= __put_user(from->_sifields._pad[0], &to->_sifields._pad[0]); | ||
68 | switch (from->si_code >> 16) { | ||
69 | case __SI_FAULT >> 16: | ||
70 | break; | ||
71 | case __SI_CHLD >> 16: | ||
72 | err |= __put_user(from->si_utime, &to->si_utime); | ||
73 | err |= __put_user(from->si_stime, &to->si_stime); | ||
74 | err |= __put_user(from->si_status, &to->si_status); | ||
75 | /* FALL THROUGH */ | ||
76 | default: | ||
77 | case __SI_KILL >> 16: | ||
78 | err |= __put_user(from->si_uid, &to->si_uid); | ||
79 | break; | ||
80 | case __SI_POLL >> 16: | ||
81 | err |= __put_user(from->si_fd, &to->si_fd); | ||
82 | break; | ||
83 | case __SI_TIMER >> 16: | ||
84 | err |= __put_user(from->si_overrun, &to->si_overrun); | ||
85 | err |= __put_user(ptr_to_compat(from->si_ptr), | ||
86 | &to->si_ptr); | ||
87 | break; | ||
88 | case __SI_RT >> 16: /* This is not generated by the kernel as of now. */ | ||
89 | case __SI_MESGQ >> 16: | ||
90 | err |= __put_user(from->si_uid, &to->si_uid); | ||
91 | err |= __put_user(from->si_int, &to->si_int); | ||
92 | break; | ||
93 | } | ||
94 | } | ||
95 | return err; | ||
96 | } | ||
97 | |||
98 | int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) | ||
99 | { | ||
100 | int err; | ||
101 | u32 ptr32; | ||
102 | if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t))) | ||
103 | return -EFAULT; | ||
104 | |||
105 | err = __get_user(to->si_signo, &from->si_signo); | ||
106 | err |= __get_user(to->si_errno, &from->si_errno); | ||
107 | err |= __get_user(to->si_code, &from->si_code); | ||
108 | |||
109 | err |= __get_user(to->si_pid, &from->si_pid); | ||
110 | err |= __get_user(to->si_uid, &from->si_uid); | ||
111 | err |= __get_user(ptr32, &from->si_ptr); | ||
112 | to->si_ptr = compat_ptr(ptr32); | ||
113 | |||
114 | return err; | ||
115 | } | ||
116 | |||
117 | asmlinkage long | ||
118 | sys32_sigsuspend(int history0, int history1, old_sigset_t mask, | ||
119 | struct pt_regs *regs) | ||
120 | { | ||
121 | sigset_t saveset; | ||
122 | |||
123 | mask &= _BLOCKABLE; | ||
124 | spin_lock_irq(¤t->sighand->siglock); | ||
125 | saveset = current->blocked; | ||
126 | siginitset(¤t->blocked, mask); | ||
127 | recalc_sigpending(); | ||
128 | spin_unlock_irq(¤t->sighand->siglock); | ||
129 | |||
130 | regs->rax = -EINTR; | ||
131 | while (1) { | ||
132 | current->state = TASK_INTERRUPTIBLE; | ||
133 | schedule(); | ||
134 | if (do_signal(regs, &saveset)) | ||
135 | return -EINTR; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | asmlinkage long | ||
140 | sys32_sigaltstack(const stack_ia32_t __user *uss_ptr, | ||
141 | stack_ia32_t __user *uoss_ptr, | ||
142 | struct pt_regs *regs) | ||
143 | { | ||
144 | stack_t uss,uoss; | ||
145 | int ret; | ||
146 | mm_segment_t seg; | ||
147 | if (uss_ptr) { | ||
148 | u32 ptr; | ||
149 | memset(&uss,0,sizeof(stack_t)); | ||
150 | if (!access_ok(VERIFY_READ,uss_ptr,sizeof(stack_ia32_t)) || | ||
151 | __get_user(ptr, &uss_ptr->ss_sp) || | ||
152 | __get_user(uss.ss_flags, &uss_ptr->ss_flags) || | ||
153 | __get_user(uss.ss_size, &uss_ptr->ss_size)) | ||
154 | return -EFAULT; | ||
155 | uss.ss_sp = compat_ptr(ptr); | ||
156 | } | ||
157 | seg = get_fs(); | ||
158 | set_fs(KERNEL_DS); | ||
159 | ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->rsp); | ||
160 | set_fs(seg); | ||
161 | if (ret >= 0 && uoss_ptr) { | ||
162 | if (!access_ok(VERIFY_WRITE,uoss_ptr,sizeof(stack_ia32_t)) || | ||
163 | __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) || | ||
164 | __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) || | ||
165 | __put_user(uoss.ss_size, &uoss_ptr->ss_size)) | ||
166 | ret = -EFAULT; | ||
167 | } | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * Do a signal return; undo the signal stack. | ||
173 | */ | ||
174 | |||
175 | struct sigframe | ||
176 | { | ||
177 | u32 pretcode; | ||
178 | int sig; | ||
179 | struct sigcontext_ia32 sc; | ||
180 | struct _fpstate_ia32 fpstate; | ||
181 | unsigned int extramask[_COMPAT_NSIG_WORDS-1]; | ||
182 | char retcode[8]; | ||
183 | }; | ||
184 | |||
185 | struct rt_sigframe | ||
186 | { | ||
187 | u32 pretcode; | ||
188 | int sig; | ||
189 | u32 pinfo; | ||
190 | u32 puc; | ||
191 | compat_siginfo_t info; | ||
192 | struct ucontext_ia32 uc; | ||
193 | struct _fpstate_ia32 fpstate; | ||
194 | char retcode[8]; | ||
195 | }; | ||
196 | |||
197 | static int | ||
198 | ia32_restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 __user *sc, unsigned int *peax) | ||
199 | { | ||
200 | unsigned int err = 0; | ||
201 | |||
202 | /* Always make any pending restarted system calls return -EINTR */ | ||
203 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
204 | |||
205 | #if DEBUG_SIG | ||
206 | printk("SIG restore_sigcontext: sc=%p err(%x) eip(%x) cs(%x) flg(%x)\n", | ||
207 | sc, sc->err, sc->eip, sc->cs, sc->eflags); | ||
208 | #endif | ||
209 | #define COPY(x) { \ | ||
210 | unsigned int reg; \ | ||
211 | err |= __get_user(reg, &sc->e ##x); \ | ||
212 | regs->r ## x = reg; \ | ||
213 | } | ||
214 | |||
215 | #define RELOAD_SEG(seg,mask) \ | ||
216 | { unsigned int cur; \ | ||
217 | unsigned short pre; \ | ||
218 | err |= __get_user(pre, &sc->seg); \ | ||
219 | asm volatile("movl %%" #seg ",%0" : "=r" (cur)); \ | ||
220 | pre |= mask; \ | ||
221 | if (pre != cur) loadsegment(seg,pre); } | ||
222 | |||
223 | /* Reload fs and gs if they have changed in the signal handler. | ||
224 | This does not handle long fs/gs base changes in the handler, but | ||
225 | does not clobber them at least in the normal case. */ | ||
226 | |||
227 | { | ||
228 | unsigned gs, oldgs; | ||
229 | err |= __get_user(gs, &sc->gs); | ||
230 | gs |= 3; | ||
231 | asm("movl %%gs,%0" : "=r" (oldgs)); | ||
232 | if (gs != oldgs) | ||
233 | load_gs_index(gs); | ||
234 | } | ||
235 | RELOAD_SEG(fs,3); | ||
236 | RELOAD_SEG(ds,3); | ||
237 | RELOAD_SEG(es,3); | ||
238 | |||
239 | COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); | ||
240 | COPY(dx); COPY(cx); COPY(ip); | ||
241 | /* Don't touch extended registers */ | ||
242 | |||
243 | err |= __get_user(regs->cs, &sc->cs); | ||
244 | regs->cs |= 3; | ||
245 | err |= __get_user(regs->ss, &sc->ss); | ||
246 | regs->ss |= 3; | ||
247 | |||
248 | { | ||
249 | unsigned int tmpflags; | ||
250 | err |= __get_user(tmpflags, &sc->eflags); | ||
251 | regs->eflags = (regs->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); | ||
252 | regs->orig_rax = -1; /* disable syscall checks */ | ||
253 | } | ||
254 | |||
255 | { | ||
256 | u32 tmp; | ||
257 | struct _fpstate_ia32 __user * buf; | ||
258 | err |= __get_user(tmp, &sc->fpstate); | ||
259 | buf = compat_ptr(tmp); | ||
260 | if (buf) { | ||
261 | if (!access_ok(VERIFY_READ, buf, sizeof(*buf))) | ||
262 | goto badframe; | ||
263 | err |= restore_i387_ia32(current, buf, 0); | ||
264 | } else { | ||
265 | struct task_struct *me = current; | ||
266 | if (used_math()) { | ||
267 | clear_fpu(me); | ||
268 | clear_used_math(); | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | { | ||
274 | u32 tmp; | ||
275 | err |= __get_user(tmp, &sc->eax); | ||
276 | *peax = tmp; | ||
277 | } | ||
278 | return err; | ||
279 | |||
280 | badframe: | ||
281 | return 1; | ||
282 | } | ||
283 | |||
284 | asmlinkage long sys32_sigreturn(struct pt_regs *regs) | ||
285 | { | ||
286 | struct sigframe __user *frame = (struct sigframe __user *)(regs->rsp-8); | ||
287 | sigset_t set; | ||
288 | unsigned int eax; | ||
289 | |||
290 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
291 | goto badframe; | ||
292 | if (__get_user(set.sig[0], &frame->sc.oldmask) | ||
293 | || (_COMPAT_NSIG_WORDS > 1 | ||
294 | && __copy_from_user((((char *) &set.sig) + 4), &frame->extramask, | ||
295 | sizeof(frame->extramask)))) | ||
296 | goto badframe; | ||
297 | |||
298 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
299 | spin_lock_irq(¤t->sighand->siglock); | ||
300 | current->blocked = set; | ||
301 | recalc_sigpending(); | ||
302 | spin_unlock_irq(¤t->sighand->siglock); | ||
303 | |||
304 | if (ia32_restore_sigcontext(regs, &frame->sc, &eax)) | ||
305 | goto badframe; | ||
306 | return eax; | ||
307 | |||
308 | badframe: | ||
309 | signal_fault(regs, frame, "32bit sigreturn"); | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) | ||
314 | { | ||
315 | struct rt_sigframe __user *frame; | ||
316 | sigset_t set; | ||
317 | unsigned int eax; | ||
318 | struct pt_regs tregs; | ||
319 | |||
320 | frame = (struct rt_sigframe __user *)(regs->rsp - 4); | ||
321 | |||
322 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
323 | goto badframe; | ||
324 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
325 | goto badframe; | ||
326 | |||
327 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
328 | spin_lock_irq(¤t->sighand->siglock); | ||
329 | current->blocked = set; | ||
330 | recalc_sigpending(); | ||
331 | spin_unlock_irq(¤t->sighand->siglock); | ||
332 | |||
333 | if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &eax)) | ||
334 | goto badframe; | ||
335 | |||
336 | tregs = *regs; | ||
337 | if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT) | ||
338 | goto badframe; | ||
339 | |||
340 | return eax; | ||
341 | |||
342 | badframe: | ||
343 | signal_fault(regs,frame,"32bit rt sigreturn"); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * Set up a signal frame. | ||
349 | */ | ||
350 | |||
351 | static int | ||
352 | ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, struct _fpstate_ia32 __user *fpstate, | ||
353 | struct pt_regs *regs, unsigned int mask) | ||
354 | { | ||
355 | int tmp, err = 0; | ||
356 | u32 eflags; | ||
357 | |||
358 | tmp = 0; | ||
359 | __asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp)); | ||
360 | err |= __put_user(tmp, (unsigned int __user *)&sc->gs); | ||
361 | __asm__("movl %%fs,%0" : "=r"(tmp): "0"(tmp)); | ||
362 | err |= __put_user(tmp, (unsigned int __user *)&sc->fs); | ||
363 | __asm__("movl %%ds,%0" : "=r"(tmp): "0"(tmp)); | ||
364 | err |= __put_user(tmp, (unsigned int __user *)&sc->ds); | ||
365 | __asm__("movl %%es,%0" : "=r"(tmp): "0"(tmp)); | ||
366 | err |= __put_user(tmp, (unsigned int __user *)&sc->es); | ||
367 | |||
368 | err |= __put_user((u32)regs->rdi, &sc->edi); | ||
369 | err |= __put_user((u32)regs->rsi, &sc->esi); | ||
370 | err |= __put_user((u32)regs->rbp, &sc->ebp); | ||
371 | err |= __put_user((u32)regs->rsp, &sc->esp); | ||
372 | err |= __put_user((u32)regs->rbx, &sc->ebx); | ||
373 | err |= __put_user((u32)regs->rdx, &sc->edx); | ||
374 | err |= __put_user((u32)regs->rcx, &sc->ecx); | ||
375 | err |= __put_user((u32)regs->rax, &sc->eax); | ||
376 | err |= __put_user((u32)regs->cs, &sc->cs); | ||
377 | err |= __put_user((u32)regs->ss, &sc->ss); | ||
378 | err |= __put_user(current->thread.trap_no, &sc->trapno); | ||
379 | err |= __put_user(current->thread.error_code, &sc->err); | ||
380 | err |= __put_user((u32)regs->rip, &sc->eip); | ||
381 | eflags = regs->eflags; | ||
382 | if (current->ptrace & PT_PTRACED) | ||
383 | eflags &= ~TF_MASK; | ||
384 | err |= __put_user((u32)eflags, &sc->eflags); | ||
385 | err |= __put_user((u32)regs->rsp, &sc->esp_at_signal); | ||
386 | |||
387 | tmp = save_i387_ia32(current, fpstate, regs, 0); | ||
388 | if (tmp < 0) | ||
389 | err = -EFAULT; | ||
390 | else { | ||
391 | clear_used_math(); | ||
392 | stts(); | ||
393 | err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL), | ||
394 | &sc->fpstate); | ||
395 | } | ||
396 | |||
397 | /* non-iBCS2 extensions.. */ | ||
398 | err |= __put_user(mask, &sc->oldmask); | ||
399 | err |= __put_user(current->thread.cr2, &sc->cr2); | ||
400 | |||
401 | return err; | ||
402 | } | ||
403 | |||
404 | /* | ||
405 | * Determine which stack to use.. | ||
406 | */ | ||
407 | static void __user * | ||
408 | get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) | ||
409 | { | ||
410 | unsigned long rsp; | ||
411 | |||
412 | /* Default to using normal stack */ | ||
413 | rsp = regs->rsp; | ||
414 | |||
415 | /* This is the X/Open sanctioned signal stack switching. */ | ||
416 | if (ka->sa.sa_flags & SA_ONSTACK) { | ||
417 | if (sas_ss_flags(rsp) == 0) | ||
418 | rsp = current->sas_ss_sp + current->sas_ss_size; | ||
419 | } | ||
420 | |||
421 | /* This is the legacy signal stack switching. */ | ||
422 | else if ((regs->ss & 0xffff) != __USER_DS && | ||
423 | !(ka->sa.sa_flags & SA_RESTORER) && | ||
424 | ka->sa.sa_restorer) { | ||
425 | rsp = (unsigned long) ka->sa.sa_restorer; | ||
426 | } | ||
427 | |||
428 | return (void __user *)((rsp - frame_size) & -8UL); | ||
429 | } | ||
430 | |||
431 | void ia32_setup_frame(int sig, struct k_sigaction *ka, | ||
432 | compat_sigset_t *set, struct pt_regs * regs) | ||
433 | { | ||
434 | struct sigframe __user *frame; | ||
435 | int err = 0; | ||
436 | |||
437 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
438 | |||
439 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
440 | goto give_sigsegv; | ||
441 | |||
442 | { | ||
443 | struct exec_domain *ed = current_thread_info()->exec_domain; | ||
444 | err |= __put_user((ed | ||
445 | && ed->signal_invmap | ||
446 | && sig < 32 | ||
447 | ? ed->signal_invmap[sig] | ||
448 | : sig), | ||
449 | &frame->sig); | ||
450 | } | ||
451 | if (err) | ||
452 | goto give_sigsegv; | ||
453 | |||
454 | err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs, | ||
455 | set->sig[0]); | ||
456 | if (err) | ||
457 | goto give_sigsegv; | ||
458 | |||
459 | if (_COMPAT_NSIG_WORDS > 1) { | ||
460 | err |= __copy_to_user(frame->extramask, &set->sig[1], | ||
461 | sizeof(frame->extramask)); | ||
462 | } | ||
463 | if (err) | ||
464 | goto give_sigsegv; | ||
465 | |||
466 | /* Return stub is in 32bit vsyscall page */ | ||
467 | { | ||
468 | void __user *restorer = VSYSCALL32_SIGRETURN; | ||
469 | if (ka->sa.sa_flags & SA_RESTORER) | ||
470 | restorer = ka->sa.sa_restorer; | ||
471 | err |= __put_user(ptr_to_compat(restorer), &frame->pretcode); | ||
472 | } | ||
473 | /* These are actually not used anymore, but left because some | ||
474 | gdb versions depend on them as a marker. */ | ||
475 | { | ||
476 | /* copy_to_user optimizes that into a single 8 byte store */ | ||
477 | static const struct { | ||
478 | u16 poplmovl; | ||
479 | u32 val; | ||
480 | u16 int80; | ||
481 | u16 pad; | ||
482 | } __attribute__((packed)) code = { | ||
483 | 0xb858, /* popl %eax ; movl $...,%eax */ | ||
484 | __NR_ia32_sigreturn, | ||
485 | 0x80cd, /* int $0x80 */ | ||
486 | 0, | ||
487 | }; | ||
488 | err |= __copy_to_user(frame->retcode, &code, 8); | ||
489 | } | ||
490 | if (err) | ||
491 | goto give_sigsegv; | ||
492 | |||
493 | /* Set up registers for signal handler */ | ||
494 | regs->rsp = (unsigned long) frame; | ||
495 | regs->rip = (unsigned long) ka->sa.sa_handler; | ||
496 | |||
497 | asm volatile("movl %0,%%ds" :: "r" (__USER32_DS)); | ||
498 | asm volatile("movl %0,%%es" :: "r" (__USER32_DS)); | ||
499 | |||
500 | regs->cs = __USER32_CS; | ||
501 | regs->ss = __USER32_DS; | ||
502 | |||
503 | set_fs(USER_DS); | ||
504 | if (regs->eflags & TF_MASK) { | ||
505 | if (current->ptrace & PT_PTRACED) { | ||
506 | ptrace_notify(SIGTRAP); | ||
507 | } else { | ||
508 | regs->eflags &= ~TF_MASK; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | #if DEBUG_SIG | ||
513 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
514 | current->comm, current->pid, frame, regs->rip, frame->pretcode); | ||
515 | #endif | ||
516 | |||
517 | return; | ||
518 | |||
519 | give_sigsegv: | ||
520 | force_sigsegv(sig, current); | ||
521 | } | ||
522 | |||
523 | void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
524 | compat_sigset_t *set, struct pt_regs * regs) | ||
525 | { | ||
526 | struct rt_sigframe __user *frame; | ||
527 | int err = 0; | ||
528 | |||
529 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
530 | |||
531 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
532 | goto give_sigsegv; | ||
533 | |||
534 | { | ||
535 | struct exec_domain *ed = current_thread_info()->exec_domain; | ||
536 | err |= __put_user((ed | ||
537 | && ed->signal_invmap | ||
538 | && sig < 32 | ||
539 | ? ed->signal_invmap[sig] | ||
540 | : sig), | ||
541 | &frame->sig); | ||
542 | } | ||
543 | err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo); | ||
544 | err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc); | ||
545 | err |= copy_siginfo_to_user32(&frame->info, info); | ||
546 | if (err) | ||
547 | goto give_sigsegv; | ||
548 | |||
549 | /* Create the ucontext. */ | ||
550 | err |= __put_user(0, &frame->uc.uc_flags); | ||
551 | err |= __put_user(0, &frame->uc.uc_link); | ||
552 | err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
553 | err |= __put_user(sas_ss_flags(regs->rsp), | ||
554 | &frame->uc.uc_stack.ss_flags); | ||
555 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
556 | err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, | ||
557 | regs, set->sig[0]); | ||
558 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
559 | if (err) | ||
560 | goto give_sigsegv; | ||
561 | |||
562 | |||
563 | { | ||
564 | void __user *restorer = VSYSCALL32_RTSIGRETURN; | ||
565 | if (ka->sa.sa_flags & SA_RESTORER) | ||
566 | restorer = ka->sa.sa_restorer; | ||
567 | err |= __put_user(ptr_to_compat(restorer), &frame->pretcode); | ||
568 | } | ||
569 | |||
570 | /* This is movl $,%eax ; int $0x80 */ | ||
571 | /* Not actually used anymore, but left because some gdb versions | ||
572 | need it. */ | ||
573 | { | ||
574 | /* __copy_to_user optimizes that into a single 8 byte store */ | ||
575 | static const struct { | ||
576 | u8 movl; | ||
577 | u32 val; | ||
578 | u16 int80; | ||
579 | u16 pad; | ||
580 | u8 pad2; | ||
581 | } __attribute__((packed)) code = { | ||
582 | 0xb8, | ||
583 | __NR_ia32_rt_sigreturn, | ||
584 | 0x80cd, | ||
585 | 0, | ||
586 | }; | ||
587 | err |= __copy_to_user(frame->retcode, &code, 8); | ||
588 | } | ||
589 | if (err) | ||
590 | goto give_sigsegv; | ||
591 | |||
592 | /* Set up registers for signal handler */ | ||
593 | regs->rsp = (unsigned long) frame; | ||
594 | regs->rip = (unsigned long) ka->sa.sa_handler; | ||
595 | |||
596 | asm volatile("movl %0,%%ds" :: "r" (__USER32_DS)); | ||
597 | asm volatile("movl %0,%%es" :: "r" (__USER32_DS)); | ||
598 | |||
599 | regs->cs = __USER32_CS; | ||
600 | regs->ss = __USER32_DS; | ||
601 | |||
602 | set_fs(USER_DS); | ||
603 | if (regs->eflags & TF_MASK) { | ||
604 | if (current->ptrace & PT_PTRACED) { | ||
605 | ptrace_notify(SIGTRAP); | ||
606 | } else { | ||
607 | regs->eflags &= ~TF_MASK; | ||
608 | } | ||
609 | } | ||
610 | |||
611 | #if DEBUG_SIG | ||
612 | printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", | ||
613 | current->comm, current->pid, frame, regs->rip, frame->pretcode); | ||
614 | #endif | ||
615 | |||
616 | return; | ||
617 | |||
618 | give_sigsegv: | ||
619 | force_sigsegv(sig, current); | ||
620 | } | ||
621 | |||
diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S new file mode 100644 index 000000000000..f3ca0db85b5b --- /dev/null +++ b/arch/x86_64/ia32/ia32entry.S | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * Compatibility mode system call entry point for x86-64. | ||
3 | * | ||
4 | * Copyright 2000-2002 Andi Kleen, SuSE Labs. | ||
5 | */ | ||
6 | |||
7 | #include <asm/dwarf2.h> | ||
8 | #include <asm/calling.h> | ||
9 | #include <asm/offset.h> | ||
10 | #include <asm/current.h> | ||
11 | #include <asm/errno.h> | ||
12 | #include <asm/ia32_unistd.h> | ||
13 | #include <asm/thread_info.h> | ||
14 | #include <asm/segment.h> | ||
15 | #include <asm/vsyscall32.h> | ||
16 | #include <linux/linkage.h> | ||
17 | |||
18 | .macro IA32_ARG_FIXUP noebp=0 | ||
19 | movl %edi,%r8d | ||
20 | .if \noebp | ||
21 | .else | ||
22 | movl %ebp,%r9d | ||
23 | .endif | ||
24 | xchg %ecx,%esi | ||
25 | movl %ebx,%edi | ||
26 | movl %edx,%edx /* zero extension */ | ||
27 | .endm | ||
28 | |||
29 | /* clobbers %eax */ | ||
30 | .macro CLEAR_RREGS | ||
31 | xorl %eax,%eax | ||
32 | movq %rax,R11(%rsp) | ||
33 | movq %rax,R10(%rsp) | ||
34 | movq %rax,R9(%rsp) | ||
35 | movq %rax,R8(%rsp) | ||
36 | .endm | ||
37 | |||
38 | /* | ||
39 | * 32bit SYSENTER instruction entry. | ||
40 | * | ||
41 | * Arguments: | ||
42 | * %eax System call number. | ||
43 | * %ebx Arg1 | ||
44 | * %ecx Arg2 | ||
45 | * %edx Arg3 | ||
46 | * %esi Arg4 | ||
47 | * %edi Arg5 | ||
48 | * %ebp user stack | ||
49 | * 0(%ebp) Arg6 | ||
50 | * | ||
51 | * Interrupts off. | ||
52 | * | ||
53 | * This is purely a fast path. For anything complicated we use the int 0x80 | ||
54 | * path below. Set up a complete hardware stack frame to share code | ||
55 | * with the int 0x80 path. | ||
56 | */ | ||
57 | ENTRY(ia32_sysenter_target) | ||
58 | CFI_STARTPROC | ||
59 | swapgs | ||
60 | movq %gs:pda_kernelstack, %rsp | ||
61 | addq $(PDA_STACKOFFSET),%rsp | ||
62 | sti | ||
63 | movl %ebp,%ebp /* zero extension */ | ||
64 | pushq $__USER32_DS | ||
65 | pushq %rbp | ||
66 | pushfq | ||
67 | movl $VSYSCALL32_SYSEXIT, %r10d | ||
68 | pushq $__USER32_CS | ||
69 | movl %eax, %eax | ||
70 | pushq %r10 | ||
71 | pushq %rax | ||
72 | cld | ||
73 | SAVE_ARGS 0,0,1 | ||
74 | /* no need to do an access_ok check here because rbp has been | ||
75 | 32bit zero extended */ | ||
76 | 1: movl (%rbp),%r9d | ||
77 | .section __ex_table,"a" | ||
78 | .quad 1b,ia32_badarg | ||
79 | .previous | ||
80 | GET_THREAD_INFO(%r10) | ||
81 | testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),threadinfo_flags(%r10) | ||
82 | jnz sysenter_tracesys | ||
83 | sysenter_do_call: | ||
84 | cmpl $(IA32_NR_syscalls),%eax | ||
85 | jae ia32_badsys | ||
86 | IA32_ARG_FIXUP 1 | ||
87 | call *ia32_sys_call_table(,%rax,8) | ||
88 | movq %rax,RAX-ARGOFFSET(%rsp) | ||
89 | GET_THREAD_INFO(%r10) | ||
90 | cli | ||
91 | testl $_TIF_ALLWORK_MASK,threadinfo_flags(%r10) | ||
92 | jnz int_ret_from_sys_call | ||
93 | /* clear IF, that popfq doesn't enable interrupts early */ | ||
94 | andl $~0x200,EFLAGS-R11(%rsp) | ||
95 | RESTORE_ARGS 1,24,1,1,1,1 | ||
96 | popfq | ||
97 | popq %rcx /* User %esp */ | ||
98 | movl $VSYSCALL32_SYSEXIT,%edx /* User %eip */ | ||
99 | swapgs | ||
100 | sti /* sti only takes effect after the next instruction */ | ||
101 | /* sysexit */ | ||
102 | .byte 0xf, 0x35 | ||
103 | |||
104 | sysenter_tracesys: | ||
105 | SAVE_REST | ||
106 | CLEAR_RREGS | ||
107 | movq $-ENOSYS,RAX(%rsp) /* really needed? */ | ||
108 | movq %rsp,%rdi /* &pt_regs -> arg1 */ | ||
109 | call syscall_trace_enter | ||
110 | LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ | ||
111 | RESTORE_REST | ||
112 | movl %ebp, %ebp | ||
113 | /* no need to do an access_ok check here because rbp has been | ||
114 | 32bit zero extended */ | ||
115 | 1: movl (%rbp),%r9d | ||
116 | .section __ex_table,"a" | ||
117 | .quad 1b,ia32_badarg | ||
118 | .previous | ||
119 | jmp sysenter_do_call | ||
120 | CFI_ENDPROC | ||
121 | |||
122 | /* | ||
123 | * 32bit SYSCALL instruction entry. | ||
124 | * | ||
125 | * Arguments: | ||
126 | * %eax System call number. | ||
127 | * %ebx Arg1 | ||
128 | * %ecx return EIP | ||
129 | * %edx Arg3 | ||
130 | * %esi Arg4 | ||
131 | * %edi Arg5 | ||
132 | * %ebp Arg2 [note: not saved in the stack frame, should not be touched] | ||
133 | * %esp user stack | ||
134 | * 0(%esp) Arg6 | ||
135 | * | ||
136 | * Interrupts off. | ||
137 | * | ||
138 | * This is purely a fast path. For anything complicated we use the int 0x80 | ||
139 | * path below. Set up a complete hardware stack frame to share code | ||
140 | * with the int 0x80 path. | ||
141 | */ | ||
142 | ENTRY(ia32_cstar_target) | ||
143 | CFI_STARTPROC | ||
144 | swapgs | ||
145 | movl %esp,%r8d | ||
146 | movq %gs:pda_kernelstack,%rsp | ||
147 | sti | ||
148 | SAVE_ARGS 8,1,1 | ||
149 | movl %eax,%eax /* zero extension */ | ||
150 | movq %rax,ORIG_RAX-ARGOFFSET(%rsp) | ||
151 | movq %rcx,RIP-ARGOFFSET(%rsp) | ||
152 | movq %rbp,RCX-ARGOFFSET(%rsp) /* this lies slightly to ptrace */ | ||
153 | movl %ebp,%ecx | ||
154 | movq $__USER32_CS,CS-ARGOFFSET(%rsp) | ||
155 | movq $__USER32_DS,SS-ARGOFFSET(%rsp) | ||
156 | movq %r11,EFLAGS-ARGOFFSET(%rsp) | ||
157 | movq %r8,RSP-ARGOFFSET(%rsp) | ||
158 | /* no need to do an access_ok check here because r8 has been | ||
159 | 32bit zero extended */ | ||
160 | /* hardware stack frame is complete now */ | ||
161 | 1: movl (%r8),%r9d | ||
162 | .section __ex_table,"a" | ||
163 | .quad 1b,ia32_badarg | ||
164 | .previous | ||
165 | GET_THREAD_INFO(%r10) | ||
166 | testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),threadinfo_flags(%r10) | ||
167 | jnz cstar_tracesys | ||
168 | cstar_do_call: | ||
169 | cmpl $IA32_NR_syscalls,%eax | ||
170 | jae ia32_badsys | ||
171 | IA32_ARG_FIXUP 1 | ||
172 | call *ia32_sys_call_table(,%rax,8) | ||
173 | movq %rax,RAX-ARGOFFSET(%rsp) | ||
174 | GET_THREAD_INFO(%r10) | ||
175 | cli | ||
176 | testl $_TIF_ALLWORK_MASK,threadinfo_flags(%r10) | ||
177 | jnz int_ret_from_sys_call | ||
178 | RESTORE_ARGS 1,-ARG_SKIP,1,1,1 | ||
179 | movl RIP-ARGOFFSET(%rsp),%ecx | ||
180 | movl EFLAGS-ARGOFFSET(%rsp),%r11d | ||
181 | movl RSP-ARGOFFSET(%rsp),%esp | ||
182 | swapgs | ||
183 | sysretl | ||
184 | |||
185 | cstar_tracesys: | ||
186 | SAVE_REST | ||
187 | CLEAR_RREGS | ||
188 | movq $-ENOSYS,RAX(%rsp) /* really needed? */ | ||
189 | movq %rsp,%rdi /* &pt_regs -> arg1 */ | ||
190 | call syscall_trace_enter | ||
191 | LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ | ||
192 | RESTORE_REST | ||
193 | movl RSP-ARGOFFSET(%rsp), %r8d | ||
194 | /* no need to do an access_ok check here because r8 has been | ||
195 | 32bit zero extended */ | ||
196 | 1: movl (%r8),%r9d | ||
197 | .section __ex_table,"a" | ||
198 | .quad 1b,ia32_badarg | ||
199 | .previous | ||
200 | jmp cstar_do_call | ||
201 | |||
202 | ia32_badarg: | ||
203 | movq $-EFAULT,%rax | ||
204 | jmp ia32_sysret | ||
205 | CFI_ENDPROC | ||
206 | |||
207 | /* | ||
208 | * Emulated IA32 system calls via int 0x80. | ||
209 | * | ||
210 | * Arguments: | ||
211 | * %eax System call number. | ||
212 | * %ebx Arg1 | ||
213 | * %ecx Arg2 | ||
214 | * %edx Arg3 | ||
215 | * %esi Arg4 | ||
216 | * %edi Arg5 | ||
217 | * %ebp Arg6 [note: not saved in the stack frame, should not be touched] | ||
218 | * | ||
219 | * Notes: | ||
220 | * Uses the same stack frame as the x86-64 version. | ||
221 | * All registers except %eax must be saved (but ptrace may violate that) | ||
222 | * Arguments are zero extended. For system calls that want sign extension and | ||
223 | * take long arguments a wrapper is needed. Most calls can just be called | ||
224 | * directly. | ||
225 | * Assumes it is only called from user space and entered with interrupts off. | ||
226 | */ | ||
227 | |||
228 | ENTRY(ia32_syscall) | ||
229 | CFI_STARTPROC | ||
230 | swapgs | ||
231 | sti | ||
232 | movl %eax,%eax | ||
233 | pushq %rax | ||
234 | cld | ||
235 | /* note the registers are not zero extended to the sf. | ||
236 | this could be a problem. */ | ||
237 | SAVE_ARGS 0,0,1 | ||
238 | GET_THREAD_INFO(%r10) | ||
239 | testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),threadinfo_flags(%r10) | ||
240 | jnz ia32_tracesys | ||
241 | ia32_do_syscall: | ||
242 | cmpl $(IA32_NR_syscalls),%eax | ||
243 | jae ia32_badsys | ||
244 | IA32_ARG_FIXUP | ||
245 | call *ia32_sys_call_table(,%rax,8) # xxx: rip relative | ||
246 | ia32_sysret: | ||
247 | movq %rax,RAX-ARGOFFSET(%rsp) | ||
248 | jmp int_ret_from_sys_call | ||
249 | |||
250 | ia32_tracesys: | ||
251 | SAVE_REST | ||
252 | movq $-ENOSYS,RAX(%rsp) /* really needed? */ | ||
253 | movq %rsp,%rdi /* &pt_regs -> arg1 */ | ||
254 | call syscall_trace_enter | ||
255 | LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ | ||
256 | RESTORE_REST | ||
257 | jmp ia32_do_syscall | ||
258 | |||
259 | ia32_badsys: | ||
260 | movq $0,ORIG_RAX-ARGOFFSET(%rsp) | ||
261 | movq $-ENOSYS,RAX-ARGOFFSET(%rsp) | ||
262 | jmp int_ret_from_sys_call | ||
263 | |||
264 | ni_syscall: | ||
265 | movq %rax,%rdi | ||
266 | jmp sys32_ni_syscall | ||
267 | |||
268 | quiet_ni_syscall: | ||
269 | movq $-ENOSYS,%rax | ||
270 | ret | ||
271 | CFI_ENDPROC | ||
272 | |||
273 | .macro PTREGSCALL label, func, arg | ||
274 | .globl \label | ||
275 | \label: | ||
276 | leaq \func(%rip),%rax | ||
277 | leaq -ARGOFFSET+8(%rsp),\arg /* 8 for return address */ | ||
278 | jmp ia32_ptregs_common | ||
279 | .endm | ||
280 | |||
281 | PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn, %rdi | ||
282 | PTREGSCALL stub32_sigreturn, sys32_sigreturn, %rdi | ||
283 | PTREGSCALL stub32_sigaltstack, sys32_sigaltstack, %rdx | ||
284 | PTREGSCALL stub32_sigsuspend, sys32_sigsuspend, %rcx | ||
285 | PTREGSCALL stub32_execve, sys32_execve, %rcx | ||
286 | PTREGSCALL stub32_fork, sys_fork, %rdi | ||
287 | PTREGSCALL stub32_clone, sys32_clone, %rdx | ||
288 | PTREGSCALL stub32_vfork, sys_vfork, %rdi | ||
289 | PTREGSCALL stub32_iopl, sys_iopl, %rsi | ||
290 | PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend, %rdx | ||
291 | |||
292 | ENTRY(ia32_ptregs_common) | ||
293 | CFI_STARTPROC | ||
294 | popq %r11 | ||
295 | SAVE_REST | ||
296 | call *%rax | ||
297 | RESTORE_REST | ||
298 | jmp ia32_sysret /* misbalances the return cache */ | ||
299 | CFI_ENDPROC | ||
300 | |||
301 | .data | ||
302 | .align 8 | ||
303 | .globl ia32_sys_call_table | ||
304 | ia32_sys_call_table: | ||
305 | .quad sys_restart_syscall | ||
306 | .quad sys_exit | ||
307 | .quad stub32_fork | ||
308 | .quad sys_read | ||
309 | .quad sys_write | ||
310 | .quad sys32_open /* 5 */ | ||
311 | .quad sys_close | ||
312 | .quad sys32_waitpid | ||
313 | .quad sys_creat | ||
314 | .quad sys_link | ||
315 | .quad sys_unlink /* 10 */ | ||
316 | .quad stub32_execve | ||
317 | .quad sys_chdir | ||
318 | .quad compat_sys_time | ||
319 | .quad sys_mknod | ||
320 | .quad sys_chmod /* 15 */ | ||
321 | .quad sys_lchown16 | ||
322 | .quad quiet_ni_syscall /* old break syscall holder */ | ||
323 | .quad sys_stat | ||
324 | .quad sys32_lseek | ||
325 | .quad sys_getpid /* 20 */ | ||
326 | .quad compat_sys_mount /* mount */ | ||
327 | .quad sys_oldumount /* old_umount */ | ||
328 | .quad sys_setuid16 | ||
329 | .quad sys_getuid16 | ||
330 | .quad compat_sys_stime /* stime */ /* 25 */ | ||
331 | .quad sys32_ptrace /* ptrace */ | ||
332 | .quad sys_alarm | ||
333 | .quad sys_fstat /* (old)fstat */ | ||
334 | .quad sys_pause | ||
335 | .quad compat_sys_utime /* 30 */ | ||
336 | .quad quiet_ni_syscall /* old stty syscall holder */ | ||
337 | .quad quiet_ni_syscall /* old gtty syscall holder */ | ||
338 | .quad sys_access | ||
339 | .quad sys_nice | ||
340 | .quad quiet_ni_syscall /* 35 */ /* old ftime syscall holder */ | ||
341 | .quad sys_sync | ||
342 | .quad sys32_kill | ||
343 | .quad sys_rename | ||
344 | .quad sys_mkdir | ||
345 | .quad sys_rmdir /* 40 */ | ||
346 | .quad sys_dup | ||
347 | .quad sys32_pipe | ||
348 | .quad compat_sys_times | ||
349 | .quad quiet_ni_syscall /* old prof syscall holder */ | ||
350 | .quad sys_brk /* 45 */ | ||
351 | .quad sys_setgid16 | ||
352 | .quad sys_getgid16 | ||
353 | .quad sys_signal | ||
354 | .quad sys_geteuid16 | ||
355 | .quad sys_getegid16 /* 50 */ | ||
356 | .quad sys_acct | ||
357 | .quad sys_umount /* new_umount */ | ||
358 | .quad quiet_ni_syscall /* old lock syscall holder */ | ||
359 | .quad compat_sys_ioctl | ||
360 | .quad compat_sys_fcntl64 /* 55 */ | ||
361 | .quad quiet_ni_syscall /* old mpx syscall holder */ | ||
362 | .quad sys_setpgid | ||
363 | .quad quiet_ni_syscall /* old ulimit syscall holder */ | ||
364 | .quad sys32_olduname | ||
365 | .quad sys_umask /* 60 */ | ||
366 | .quad sys_chroot | ||
367 | .quad sys32_ustat | ||
368 | .quad sys_dup2 | ||
369 | .quad sys_getppid | ||
370 | .quad sys_getpgrp /* 65 */ | ||
371 | .quad sys_setsid | ||
372 | .quad sys32_sigaction | ||
373 | .quad sys_sgetmask | ||
374 | .quad sys_ssetmask | ||
375 | .quad sys_setreuid16 /* 70 */ | ||
376 | .quad sys_setregid16 | ||
377 | .quad stub32_sigsuspend | ||
378 | .quad compat_sys_sigpending | ||
379 | .quad sys_sethostname | ||
380 | .quad compat_sys_setrlimit /* 75 */ | ||
381 | .quad compat_sys_old_getrlimit /* old_getrlimit */ | ||
382 | .quad compat_sys_getrusage | ||
383 | .quad sys32_gettimeofday | ||
384 | .quad sys32_settimeofday | ||
385 | .quad sys_getgroups16 /* 80 */ | ||
386 | .quad sys_setgroups16 | ||
387 | .quad sys32_old_select | ||
388 | .quad sys_symlink | ||
389 | .quad sys_lstat | ||
390 | .quad sys_readlink /* 85 */ | ||
391 | #ifdef CONFIG_IA32_AOUT | ||
392 | .quad sys_uselib | ||
393 | #else | ||
394 | .quad quiet_ni_syscall | ||
395 | #endif | ||
396 | .quad sys_swapon | ||
397 | .quad sys_reboot | ||
398 | .quad compat_sys_old_readdir | ||
399 | .quad sys32_mmap /* 90 */ | ||
400 | .quad sys_munmap | ||
401 | .quad sys_truncate | ||
402 | .quad sys_ftruncate | ||
403 | .quad sys_fchmod | ||
404 | .quad sys_fchown16 /* 95 */ | ||
405 | .quad sys_getpriority | ||
406 | .quad sys_setpriority | ||
407 | .quad quiet_ni_syscall /* old profil syscall holder */ | ||
408 | .quad compat_sys_statfs | ||
409 | .quad compat_sys_fstatfs /* 100 */ | ||
410 | .quad sys_ioperm | ||
411 | .quad compat_sys_socketcall | ||
412 | .quad sys_syslog | ||
413 | .quad compat_sys_setitimer | ||
414 | .quad compat_sys_getitimer /* 105 */ | ||
415 | .quad compat_sys_newstat | ||
416 | .quad compat_sys_newlstat | ||
417 | .quad compat_sys_newfstat | ||
418 | .quad sys32_uname | ||
419 | .quad stub32_iopl /* 110 */ | ||
420 | .quad sys_vhangup | ||
421 | .quad quiet_ni_syscall /* old "idle" system call */ | ||
422 | .quad sys32_vm86_warning /* vm86old */ | ||
423 | .quad compat_sys_wait4 | ||
424 | .quad sys_swapoff /* 115 */ | ||
425 | .quad sys32_sysinfo | ||
426 | .quad sys32_ipc | ||
427 | .quad sys_fsync | ||
428 | .quad stub32_sigreturn | ||
429 | .quad stub32_clone /* 120 */ | ||
430 | .quad sys_setdomainname | ||
431 | .quad sys_uname | ||
432 | .quad sys_modify_ldt | ||
433 | .quad sys32_adjtimex | ||
434 | .quad sys32_mprotect /* 125 */ | ||
435 | .quad compat_sys_sigprocmask | ||
436 | .quad quiet_ni_syscall /* create_module */ | ||
437 | .quad sys_init_module | ||
438 | .quad sys_delete_module | ||
439 | .quad quiet_ni_syscall /* 130 get_kernel_syms */ | ||
440 | .quad sys_quotactl | ||
441 | .quad sys_getpgid | ||
442 | .quad sys_fchdir | ||
443 | .quad quiet_ni_syscall /* bdflush */ | ||
444 | .quad sys_sysfs /* 135 */ | ||
445 | .quad sys_personality | ||
446 | .quad quiet_ni_syscall /* for afs_syscall */ | ||
447 | .quad sys_setfsuid16 | ||
448 | .quad sys_setfsgid16 | ||
449 | .quad sys_llseek /* 140 */ | ||
450 | .quad compat_sys_getdents | ||
451 | .quad compat_sys_select | ||
452 | .quad sys_flock | ||
453 | .quad sys_msync | ||
454 | .quad compat_sys_readv /* 145 */ | ||
455 | .quad compat_sys_writev | ||
456 | .quad sys_getsid | ||
457 | .quad sys_fdatasync | ||
458 | .quad sys32_sysctl /* sysctl */ | ||
459 | .quad sys_mlock /* 150 */ | ||
460 | .quad sys_munlock | ||
461 | .quad sys_mlockall | ||
462 | .quad sys_munlockall | ||
463 | .quad sys_sched_setparam | ||
464 | .quad sys_sched_getparam /* 155 */ | ||
465 | .quad sys_sched_setscheduler | ||
466 | .quad sys_sched_getscheduler | ||
467 | .quad sys_sched_yield | ||
468 | .quad sys_sched_get_priority_max | ||
469 | .quad sys_sched_get_priority_min /* 160 */ | ||
470 | .quad sys_sched_rr_get_interval | ||
471 | .quad compat_sys_nanosleep | ||
472 | .quad sys_mremap | ||
473 | .quad sys_setresuid16 | ||
474 | .quad sys_getresuid16 /* 165 */ | ||
475 | .quad sys32_vm86_warning /* vm86 */ | ||
476 | .quad quiet_ni_syscall /* query_module */ | ||
477 | .quad sys_poll | ||
478 | .quad compat_sys_nfsservctl | ||
479 | .quad sys_setresgid16 /* 170 */ | ||
480 | .quad sys_getresgid16 | ||
481 | .quad sys_prctl | ||
482 | .quad stub32_rt_sigreturn | ||
483 | .quad sys32_rt_sigaction | ||
484 | .quad sys32_rt_sigprocmask /* 175 */ | ||
485 | .quad sys32_rt_sigpending | ||
486 | .quad compat_sys_rt_sigtimedwait | ||
487 | .quad sys32_rt_sigqueueinfo | ||
488 | .quad stub32_rt_sigsuspend | ||
489 | .quad sys32_pread /* 180 */ | ||
490 | .quad sys32_pwrite | ||
491 | .quad sys_chown16 | ||
492 | .quad sys_getcwd | ||
493 | .quad sys_capget | ||
494 | .quad sys_capset | ||
495 | .quad stub32_sigaltstack | ||
496 | .quad sys32_sendfile | ||
497 | .quad quiet_ni_syscall /* streams1 */ | ||
498 | .quad quiet_ni_syscall /* streams2 */ | ||
499 | .quad stub32_vfork /* 190 */ | ||
500 | .quad compat_sys_getrlimit | ||
501 | .quad sys32_mmap2 | ||
502 | .quad sys32_truncate64 | ||
503 | .quad sys32_ftruncate64 | ||
504 | .quad sys32_stat64 /* 195 */ | ||
505 | .quad sys32_lstat64 | ||
506 | .quad sys32_fstat64 | ||
507 | .quad sys_lchown | ||
508 | .quad sys_getuid | ||
509 | .quad sys_getgid /* 200 */ | ||
510 | .quad sys_geteuid | ||
511 | .quad sys_getegid | ||
512 | .quad sys_setreuid | ||
513 | .quad sys_setregid | ||
514 | .quad sys_getgroups /* 205 */ | ||
515 | .quad sys_setgroups | ||
516 | .quad sys_fchown | ||
517 | .quad sys_setresuid | ||
518 | .quad sys_getresuid | ||
519 | .quad sys_setresgid /* 210 */ | ||
520 | .quad sys_getresgid | ||
521 | .quad sys_chown | ||
522 | .quad sys_setuid | ||
523 | .quad sys_setgid | ||
524 | .quad sys_setfsuid /* 215 */ | ||
525 | .quad sys_setfsgid | ||
526 | .quad sys_pivot_root | ||
527 | .quad sys_mincore | ||
528 | .quad sys_madvise | ||
529 | .quad compat_sys_getdents64 /* 220 getdents64 */ | ||
530 | .quad compat_sys_fcntl64 | ||
531 | .quad quiet_ni_syscall /* tux */ | ||
532 | .quad quiet_ni_syscall /* security */ | ||
533 | .quad sys_gettid | ||
534 | .quad sys_readahead /* 225 */ | ||
535 | .quad sys_setxattr | ||
536 | .quad sys_lsetxattr | ||
537 | .quad sys_fsetxattr | ||
538 | .quad sys_getxattr | ||
539 | .quad sys_lgetxattr /* 230 */ | ||
540 | .quad sys_fgetxattr | ||
541 | .quad sys_listxattr | ||
542 | .quad sys_llistxattr | ||
543 | .quad sys_flistxattr | ||
544 | .quad sys_removexattr /* 235 */ | ||
545 | .quad sys_lremovexattr | ||
546 | .quad sys_fremovexattr | ||
547 | .quad sys_tkill | ||
548 | .quad sys_sendfile64 | ||
549 | .quad compat_sys_futex /* 240 */ | ||
550 | .quad compat_sys_sched_setaffinity | ||
551 | .quad compat_sys_sched_getaffinity | ||
552 | .quad sys32_set_thread_area | ||
553 | .quad sys32_get_thread_area | ||
554 | .quad compat_sys_io_setup /* 245 */ | ||
555 | .quad sys_io_destroy | ||
556 | .quad compat_sys_io_getevents | ||
557 | .quad compat_sys_io_submit | ||
558 | .quad sys_io_cancel | ||
559 | .quad sys_fadvise64 /* 250 */ | ||
560 | .quad quiet_ni_syscall /* free_huge_pages */ | ||
561 | .quad sys_exit_group | ||
562 | .quad sys32_lookup_dcookie | ||
563 | .quad sys_epoll_create | ||
564 | .quad sys_epoll_ctl /* 255 */ | ||
565 | .quad sys_epoll_wait | ||
566 | .quad sys_remap_file_pages | ||
567 | .quad sys_set_tid_address | ||
568 | .quad sys32_timer_create | ||
569 | .quad compat_sys_timer_settime /* 260 */ | ||
570 | .quad compat_sys_timer_gettime | ||
571 | .quad sys_timer_getoverrun | ||
572 | .quad sys_timer_delete | ||
573 | .quad compat_sys_clock_settime | ||
574 | .quad compat_sys_clock_gettime /* 265 */ | ||
575 | .quad compat_sys_clock_getres | ||
576 | .quad compat_sys_clock_nanosleep | ||
577 | .quad compat_sys_statfs64 | ||
578 | .quad compat_sys_fstatfs64 | ||
579 | .quad sys_tgkill /* 270 */ | ||
580 | .quad compat_sys_utimes | ||
581 | .quad sys32_fadvise64_64 | ||
582 | .quad quiet_ni_syscall /* sys_vserver */ | ||
583 | .quad sys_mbind | ||
584 | .quad compat_sys_get_mempolicy /* 275 */ | ||
585 | .quad sys_set_mempolicy | ||
586 | .quad compat_sys_mq_open | ||
587 | .quad sys_mq_unlink | ||
588 | .quad compat_sys_mq_timedsend | ||
589 | .quad compat_sys_mq_timedreceive /* 280 */ | ||
590 | .quad compat_sys_mq_notify | ||
591 | .quad compat_sys_mq_getsetattr | ||
592 | .quad quiet_ni_syscall /* reserved for kexec */ | ||
593 | .quad compat_sys_waitid | ||
594 | .quad quiet_ni_syscall /* sys_altroot */ | ||
595 | .quad sys_add_key | ||
596 | .quad sys_request_key | ||
597 | .quad sys_keyctl | ||
598 | /* don't forget to change IA32_NR_syscalls */ | ||
599 | ia32_syscall_end: | ||
600 | .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 | ||
601 | .quad ni_syscall | ||
602 | .endr | ||
diff --git a/arch/x86_64/ia32/ipc32.c b/arch/x86_64/ia32/ipc32.c new file mode 100644 index 000000000000..369151dc3213 --- /dev/null +++ b/arch/x86_64/ia32/ipc32.c | |||
@@ -0,0 +1,57 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/spinlock.h> | ||
3 | #include <linux/list.h> | ||
4 | #include <linux/syscalls.h> | ||
5 | #include <linux/time.h> | ||
6 | #include <linux/sem.h> | ||
7 | #include <linux/msg.h> | ||
8 | #include <linux/shm.h> | ||
9 | #include <linux/ipc.h> | ||
10 | #include <linux/compat.h> | ||
11 | |||
12 | #include <asm-i386/ipc.h> | ||
13 | |||
14 | asmlinkage long | ||
15 | sys32_ipc(u32 call, int first, int second, int third, | ||
16 | compat_uptr_t ptr, u32 fifth) | ||
17 | { | ||
18 | int version; | ||
19 | |||
20 | version = call >> 16; /* hack for backward compatibility */ | ||
21 | call &= 0xffff; | ||
22 | |||
23 | switch (call) { | ||
24 | case SEMOP: | ||
25 | /* struct sembuf is the same on 32 and 64bit :)) */ | ||
26 | return sys_semtimedop(first, compat_ptr(ptr), second, NULL); | ||
27 | case SEMTIMEDOP: | ||
28 | return compat_sys_semtimedop(first, compat_ptr(ptr), second, | ||
29 | compat_ptr(fifth)); | ||
30 | case SEMGET: | ||
31 | return sys_semget(first, second, third); | ||
32 | case SEMCTL: | ||
33 | return compat_sys_semctl(first, second, third, compat_ptr(ptr)); | ||
34 | |||
35 | case MSGSND: | ||
36 | return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); | ||
37 | case MSGRCV: | ||
38 | return compat_sys_msgrcv(first, second, fifth, third, | ||
39 | version, compat_ptr(ptr)); | ||
40 | case MSGGET: | ||
41 | return sys_msgget((key_t) first, second); | ||
42 | case MSGCTL: | ||
43 | return compat_sys_msgctl(first, second, compat_ptr(ptr)); | ||
44 | |||
45 | case SHMAT: | ||
46 | return compat_sys_shmat(first, second, third, version, | ||
47 | compat_ptr(ptr)); | ||
48 | break; | ||
49 | case SHMDT: | ||
50 | return sys_shmdt(compat_ptr(ptr)); | ||
51 | case SHMGET: | ||
52 | return sys_shmget(first, (unsigned)second, third); | ||
53 | case SHMCTL: | ||
54 | return compat_sys_shmctl(first, second, compat_ptr(ptr)); | ||
55 | } | ||
56 | return -ENOSYS; | ||
57 | } | ||
diff --git a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c new file mode 100644 index 000000000000..b98b6d2462f6 --- /dev/null +++ b/arch/x86_64/ia32/ptrace32.c | |||
@@ -0,0 +1,379 @@ | |||
1 | /* | ||
2 | * 32bit ptrace for x86-64. | ||
3 | * | ||
4 | * Copyright 2001,2002 Andi Kleen, SuSE Labs. | ||
5 | * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier | ||
6 | * copyright. | ||
7 | * | ||
8 | * This allows to access 64bit processes too; but there is no way to see the extended | ||
9 | * register contents. | ||
10 | * | ||
11 | * $Id: ptrace32.c,v 1.16 2003/03/14 16:06:35 ak Exp $ | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/stddef.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/syscalls.h> | ||
18 | #include <linux/unistd.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/ptrace.h> | ||
21 | #include <asm/ptrace.h> | ||
22 | #include <asm/compat.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/user32.h> | ||
25 | #include <asm/user.h> | ||
26 | #include <asm/errno.h> | ||
27 | #include <asm/debugreg.h> | ||
28 | #include <asm/i387.h> | ||
29 | #include <asm/fpu32.h> | ||
30 | |||
31 | /* determines which flags the user has access to. */ | ||
32 | /* 1 = access 0 = no access */ | ||
33 | #define FLAG_MASK 0x44dd5UL | ||
34 | |||
35 | #define R32(l,q) \ | ||
36 | case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break | ||
37 | |||
38 | static int putreg32(struct task_struct *child, unsigned regno, u32 val) | ||
39 | { | ||
40 | int i; | ||
41 | __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); | ||
42 | |||
43 | switch (regno) { | ||
44 | case offsetof(struct user32, regs.fs): | ||
45 | if (val && (val & 3) != 3) return -EIO; | ||
46 | child->thread.fs = val & 0xffff; | ||
47 | break; | ||
48 | case offsetof(struct user32, regs.gs): | ||
49 | if (val && (val & 3) != 3) return -EIO; | ||
50 | child->thread.gs = val & 0xffff; | ||
51 | break; | ||
52 | case offsetof(struct user32, regs.ds): | ||
53 | if (val && (val & 3) != 3) return -EIO; | ||
54 | child->thread.ds = val & 0xffff; | ||
55 | break; | ||
56 | case offsetof(struct user32, regs.es): | ||
57 | child->thread.es = val & 0xffff; | ||
58 | break; | ||
59 | case offsetof(struct user32, regs.ss): | ||
60 | if ((val & 3) != 3) return -EIO; | ||
61 | stack[offsetof(struct pt_regs, ss)/8] = val & 0xffff; | ||
62 | break; | ||
63 | case offsetof(struct user32, regs.cs): | ||
64 | if ((val & 3) != 3) return -EIO; | ||
65 | stack[offsetof(struct pt_regs, cs)/8] = val & 0xffff; | ||
66 | break; | ||
67 | |||
68 | R32(ebx, rbx); | ||
69 | R32(ecx, rcx); | ||
70 | R32(edx, rdx); | ||
71 | R32(edi, rdi); | ||
72 | R32(esi, rsi); | ||
73 | R32(ebp, rbp); | ||
74 | R32(eax, rax); | ||
75 | R32(orig_eax, orig_rax); | ||
76 | R32(eip, rip); | ||
77 | R32(esp, rsp); | ||
78 | |||
79 | case offsetof(struct user32, regs.eflags): { | ||
80 | __u64 *flags = &stack[offsetof(struct pt_regs, eflags)/8]; | ||
81 | val &= FLAG_MASK; | ||
82 | *flags = val | (*flags & ~FLAG_MASK); | ||
83 | break; | ||
84 | } | ||
85 | |||
86 | case offsetof(struct user32, u_debugreg[4]): | ||
87 | case offsetof(struct user32, u_debugreg[5]): | ||
88 | return -EIO; | ||
89 | |||
90 | case offsetof(struct user32, u_debugreg[0]): | ||
91 | child->thread.debugreg0 = val; | ||
92 | break; | ||
93 | |||
94 | case offsetof(struct user32, u_debugreg[1]): | ||
95 | child->thread.debugreg1 = val; | ||
96 | break; | ||
97 | |||
98 | case offsetof(struct user32, u_debugreg[2]): | ||
99 | child->thread.debugreg2 = val; | ||
100 | break; | ||
101 | |||
102 | case offsetof(struct user32, u_debugreg[3]): | ||
103 | child->thread.debugreg3 = val; | ||
104 | break; | ||
105 | |||
106 | case offsetof(struct user32, u_debugreg[6]): | ||
107 | child->thread.debugreg6 = val; | ||
108 | break; | ||
109 | |||
110 | case offsetof(struct user32, u_debugreg[7]): | ||
111 | val &= ~DR_CONTROL_RESERVED; | ||
112 | /* See arch/i386/kernel/ptrace.c for an explanation of | ||
113 | * this awkward check.*/ | ||
114 | for(i=0; i<4; i++) | ||
115 | if ((0x5454 >> ((val >> (16 + 4*i)) & 0xf)) & 1) | ||
116 | return -EIO; | ||
117 | child->thread.debugreg7 = val; | ||
118 | break; | ||
119 | |||
120 | default: | ||
121 | if (regno > sizeof(struct user32) || (regno & 3)) | ||
122 | return -EIO; | ||
123 | |||
124 | /* Other dummy fields in the virtual user structure are ignored */ | ||
125 | break; | ||
126 | } | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | #undef R32 | ||
131 | |||
132 | #define R32(l,q) \ | ||
133 | case offsetof(struct user32, regs.l): *val = stack[offsetof(struct pt_regs, q)/8]; break | ||
134 | |||
135 | static int getreg32(struct task_struct *child, unsigned regno, u32 *val) | ||
136 | { | ||
137 | __u64 *stack = (__u64 *)(child->thread.rsp0 - sizeof(struct pt_regs)); | ||
138 | |||
139 | switch (regno) { | ||
140 | case offsetof(struct user32, regs.fs): | ||
141 | *val = child->thread.fs; | ||
142 | break; | ||
143 | case offsetof(struct user32, regs.gs): | ||
144 | *val = child->thread.gs; | ||
145 | break; | ||
146 | case offsetof(struct user32, regs.ds): | ||
147 | *val = child->thread.ds; | ||
148 | break; | ||
149 | case offsetof(struct user32, regs.es): | ||
150 | *val = child->thread.es; | ||
151 | break; | ||
152 | |||
153 | R32(cs, cs); | ||
154 | R32(ss, ss); | ||
155 | R32(ebx, rbx); | ||
156 | R32(ecx, rcx); | ||
157 | R32(edx, rdx); | ||
158 | R32(edi, rdi); | ||
159 | R32(esi, rsi); | ||
160 | R32(ebp, rbp); | ||
161 | R32(eax, rax); | ||
162 | R32(orig_eax, orig_rax); | ||
163 | R32(eip, rip); | ||
164 | R32(eflags, eflags); | ||
165 | R32(esp, rsp); | ||
166 | |||
167 | case offsetof(struct user32, u_debugreg[0]): | ||
168 | *val = child->thread.debugreg0; | ||
169 | break; | ||
170 | case offsetof(struct user32, u_debugreg[1]): | ||
171 | *val = child->thread.debugreg1; | ||
172 | break; | ||
173 | case offsetof(struct user32, u_debugreg[2]): | ||
174 | *val = child->thread.debugreg2; | ||
175 | break; | ||
176 | case offsetof(struct user32, u_debugreg[3]): | ||
177 | *val = child->thread.debugreg3; | ||
178 | break; | ||
179 | case offsetof(struct user32, u_debugreg[6]): | ||
180 | *val = child->thread.debugreg6; | ||
181 | break; | ||
182 | case offsetof(struct user32, u_debugreg[7]): | ||
183 | *val = child->thread.debugreg7; | ||
184 | break; | ||
185 | |||
186 | default: | ||
187 | if (regno > sizeof(struct user32) || (regno & 3)) | ||
188 | return -EIO; | ||
189 | |||
190 | /* Other dummy fields in the virtual user structure are ignored */ | ||
191 | *val = 0; | ||
192 | break; | ||
193 | } | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | #undef R32 | ||
198 | |||
199 | static struct task_struct *find_target(int request, int pid, int *err) | ||
200 | { | ||
201 | struct task_struct *child; | ||
202 | |||
203 | *err = -EPERM; | ||
204 | if (pid == 1) | ||
205 | return NULL; | ||
206 | |||
207 | *err = -ESRCH; | ||
208 | read_lock(&tasklist_lock); | ||
209 | child = find_task_by_pid(pid); | ||
210 | if (child) | ||
211 | get_task_struct(child); | ||
212 | read_unlock(&tasklist_lock); | ||
213 | if (child) { | ||
214 | *err = -EPERM; | ||
215 | if (child->pid == 1) | ||
216 | goto out; | ||
217 | *err = ptrace_check_attach(child, request == PTRACE_KILL); | ||
218 | if (*err < 0) | ||
219 | goto out; | ||
220 | return child; | ||
221 | } | ||
222 | out: | ||
223 | if (child) | ||
224 | put_task_struct(child); | ||
225 | return NULL; | ||
226 | |||
227 | } | ||
228 | |||
229 | asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) | ||
230 | { | ||
231 | struct task_struct *child; | ||
232 | struct pt_regs *childregs; | ||
233 | void __user *datap = compat_ptr(data); | ||
234 | int ret; | ||
235 | __u32 val; | ||
236 | |||
237 | switch (request) { | ||
238 | default: | ||
239 | return sys_ptrace(request, pid, addr, data); | ||
240 | |||
241 | case PTRACE_PEEKTEXT: | ||
242 | case PTRACE_PEEKDATA: | ||
243 | case PTRACE_POKEDATA: | ||
244 | case PTRACE_POKETEXT: | ||
245 | case PTRACE_POKEUSR: | ||
246 | case PTRACE_PEEKUSR: | ||
247 | case PTRACE_GETREGS: | ||
248 | case PTRACE_SETREGS: | ||
249 | case PTRACE_SETFPREGS: | ||
250 | case PTRACE_GETFPREGS: | ||
251 | case PTRACE_SETFPXREGS: | ||
252 | case PTRACE_GETFPXREGS: | ||
253 | case PTRACE_GETEVENTMSG: | ||
254 | break; | ||
255 | } | ||
256 | |||
257 | child = find_target(request, pid, &ret); | ||
258 | if (!child) | ||
259 | return ret; | ||
260 | |||
261 | childregs = (struct pt_regs *)(child->thread.rsp0 - sizeof(struct pt_regs)); | ||
262 | |||
263 | switch (request) { | ||
264 | case PTRACE_PEEKDATA: | ||
265 | case PTRACE_PEEKTEXT: | ||
266 | ret = 0; | ||
267 | if (access_process_vm(child, addr, &val, sizeof(u32), 0)!=sizeof(u32)) | ||
268 | ret = -EIO; | ||
269 | else | ||
270 | ret = put_user(val, (unsigned int __user *)datap); | ||
271 | break; | ||
272 | |||
273 | case PTRACE_POKEDATA: | ||
274 | case PTRACE_POKETEXT: | ||
275 | ret = 0; | ||
276 | if (access_process_vm(child, addr, &data, sizeof(u32), 1)!=sizeof(u32)) | ||
277 | ret = -EIO; | ||
278 | break; | ||
279 | |||
280 | case PTRACE_PEEKUSR: | ||
281 | ret = getreg32(child, addr, &val); | ||
282 | if (ret == 0) | ||
283 | ret = put_user(val, (__u32 __user *)datap); | ||
284 | break; | ||
285 | |||
286 | case PTRACE_POKEUSR: | ||
287 | ret = putreg32(child, addr, data); | ||
288 | break; | ||
289 | |||
290 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | ||
291 | int i; | ||
292 | if (!access_ok(VERIFY_WRITE, datap, 16*4)) { | ||
293 | ret = -EIO; | ||
294 | break; | ||
295 | } | ||
296 | ret = 0; | ||
297 | for ( i = 0; i <= 16*4 ; i += sizeof(__u32) ) { | ||
298 | getreg32(child, i, &val); | ||
299 | ret |= __put_user(val,(u32 __user *)datap); | ||
300 | datap += sizeof(u32); | ||
301 | } | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | ||
306 | unsigned long tmp; | ||
307 | int i; | ||
308 | if (!access_ok(VERIFY_READ, datap, 16*4)) { | ||
309 | ret = -EIO; | ||
310 | break; | ||
311 | } | ||
312 | ret = 0; | ||
313 | for ( i = 0; i <= 16*4; i += sizeof(u32) ) { | ||
314 | ret |= __get_user(tmp, (u32 __user *)datap); | ||
315 | putreg32(child, i, tmp); | ||
316 | datap += sizeof(u32); | ||
317 | } | ||
318 | break; | ||
319 | } | ||
320 | |||
321 | case PTRACE_GETFPREGS: | ||
322 | ret = -EIO; | ||
323 | if (!access_ok(VERIFY_READ, compat_ptr(data), | ||
324 | sizeof(struct user_i387_struct))) | ||
325 | break; | ||
326 | save_i387_ia32(child, datap, childregs, 1); | ||
327 | ret = 0; | ||
328 | break; | ||
329 | |||
330 | case PTRACE_SETFPREGS: | ||
331 | ret = -EIO; | ||
332 | if (!access_ok(VERIFY_WRITE, datap, | ||
333 | sizeof(struct user_i387_struct))) | ||
334 | break; | ||
335 | ret = 0; | ||
336 | /* don't check EFAULT to be bug-to-bug compatible to i386 */ | ||
337 | restore_i387_ia32(child, datap, 1); | ||
338 | break; | ||
339 | |||
340 | case PTRACE_GETFPXREGS: { | ||
341 | struct user32_fxsr_struct __user *u = datap; | ||
342 | init_fpu(child); | ||
343 | ret = -EIO; | ||
344 | if (!access_ok(VERIFY_WRITE, u, sizeof(*u))) | ||
345 | break; | ||
346 | ret = -EFAULT; | ||
347 | if (__copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u))) | ||
348 | break; | ||
349 | ret = __put_user(childregs->cs, &u->fcs); | ||
350 | ret |= __put_user(child->thread.ds, &u->fos); | ||
351 | break; | ||
352 | } | ||
353 | case PTRACE_SETFPXREGS: { | ||
354 | struct user32_fxsr_struct __user *u = datap; | ||
355 | unlazy_fpu(child); | ||
356 | ret = -EIO; | ||
357 | if (!access_ok(VERIFY_READ, u, sizeof(*u))) | ||
358 | break; | ||
359 | /* no checking to be bug-to-bug compatible with i386 */ | ||
360 | __copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u)); | ||
361 | set_stopped_child_used_math(child); | ||
362 | child->thread.i387.fxsave.mxcsr &= mxcsr_feature_mask; | ||
363 | ret = 0; | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | case PTRACE_GETEVENTMSG: | ||
368 | ret = put_user(child->ptrace_message,(unsigned int __user *)compat_ptr(data)); | ||
369 | break; | ||
370 | |||
371 | default: | ||
372 | ret = -EINVAL; | ||
373 | break; | ||
374 | } | ||
375 | |||
376 | put_task_struct(child); | ||
377 | return ret; | ||
378 | } | ||
379 | |||
diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c new file mode 100644 index 000000000000..68a9ab06ee7c --- /dev/null +++ b/arch/x86_64/ia32/sys_ia32.c | |||
@@ -0,0 +1,1050 @@ | |||
1 | /* | ||
2 | * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on | ||
3 | * sys_sparc32 | ||
4 | * | ||
5 | * Copyright (C) 2000 VA Linux Co | ||
6 | * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> | ||
7 | * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> | ||
8 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | ||
9 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | ||
10 | * Copyright (C) 2000 Hewlett-Packard Co. | ||
11 | * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> | ||
12 | * Copyright (C) 2000,2001,2002 Andi Kleen, SuSE Labs (x86-64 port) | ||
13 | * | ||
14 | * These routines maintain argument size conversion between 32bit and 64bit | ||
15 | * environment. In 2.5 most of this should be moved to a generic directory. | ||
16 | * | ||
17 | * This file assumes that there is a hole at the end of user address space. | ||
18 | * | ||
19 | * Some of the functions are LE specific currently. These are hopefully all marked. | ||
20 | * This should be fixed. | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/fs.h> | ||
27 | #include <linux/file.h> | ||
28 | #include <linux/signal.h> | ||
29 | #include <linux/syscalls.h> | ||
30 | #include <linux/resource.h> | ||
31 | #include <linux/times.h> | ||
32 | #include <linux/utsname.h> | ||
33 | #include <linux/timex.h> | ||
34 | #include <linux/smp.h> | ||
35 | #include <linux/smp_lock.h> | ||
36 | #include <linux/sem.h> | ||
37 | #include <linux/msg.h> | ||
38 | #include <linux/mm.h> | ||
39 | #include <linux/shm.h> | ||
40 | #include <linux/slab.h> | ||
41 | #include <linux/uio.h> | ||
42 | #include <linux/nfs_fs.h> | ||
43 | #include <linux/quota.h> | ||
44 | #include <linux/module.h> | ||
45 | #include <linux/sunrpc/svc.h> | ||
46 | #include <linux/nfsd/nfsd.h> | ||
47 | #include <linux/nfsd/cache.h> | ||
48 | #include <linux/nfsd/xdr.h> | ||
49 | #include <linux/nfsd/syscall.h> | ||
50 | #include <linux/poll.h> | ||
51 | #include <linux/personality.h> | ||
52 | #include <linux/stat.h> | ||
53 | #include <linux/ipc.h> | ||
54 | #include <linux/rwsem.h> | ||
55 | #include <linux/binfmts.h> | ||
56 | #include <linux/init.h> | ||
57 | #include <linux/aio_abi.h> | ||
58 | #include <linux/aio.h> | ||
59 | #include <linux/compat.h> | ||
60 | #include <linux/vfs.h> | ||
61 | #include <linux/ptrace.h> | ||
62 | #include <linux/highuid.h> | ||
63 | #include <linux/vmalloc.h> | ||
64 | #include <asm/mman.h> | ||
65 | #include <asm/types.h> | ||
66 | #include <asm/uaccess.h> | ||
67 | #include <asm/semaphore.h> | ||
68 | #include <asm/atomic.h> | ||
69 | #include <asm/ldt.h> | ||
70 | |||
71 | #include <net/scm.h> | ||
72 | #include <net/sock.h> | ||
73 | #include <asm/ia32.h> | ||
74 | |||
75 | #define AA(__x) ((unsigned long)(__x)) | ||
76 | |||
77 | int cp_compat_stat(struct kstat *kbuf, struct compat_stat __user *ubuf) | ||
78 | { | ||
79 | typeof(ubuf->st_uid) uid = 0; | ||
80 | typeof(ubuf->st_gid) gid = 0; | ||
81 | SET_UID(uid, kbuf->uid); | ||
82 | SET_GID(gid, kbuf->gid); | ||
83 | if (!old_valid_dev(kbuf->dev) || !old_valid_dev(kbuf->rdev)) | ||
84 | return -EOVERFLOW; | ||
85 | if (kbuf->size >= 0x7fffffff) | ||
86 | return -EOVERFLOW; | ||
87 | if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct compat_stat)) || | ||
88 | __put_user (old_encode_dev(kbuf->dev), &ubuf->st_dev) || | ||
89 | __put_user (kbuf->ino, &ubuf->st_ino) || | ||
90 | __put_user (kbuf->mode, &ubuf->st_mode) || | ||
91 | __put_user (kbuf->nlink, &ubuf->st_nlink) || | ||
92 | __put_user (uid, &ubuf->st_uid) || | ||
93 | __put_user (gid, &ubuf->st_gid) || | ||
94 | __put_user (old_encode_dev(kbuf->rdev), &ubuf->st_rdev) || | ||
95 | __put_user (kbuf->size, &ubuf->st_size) || | ||
96 | __put_user (kbuf->atime.tv_sec, &ubuf->st_atime) || | ||
97 | __put_user (kbuf->atime.tv_nsec, &ubuf->st_atime_nsec) || | ||
98 | __put_user (kbuf->mtime.tv_sec, &ubuf->st_mtime) || | ||
99 | __put_user (kbuf->mtime.tv_nsec, &ubuf->st_mtime_nsec) || | ||
100 | __put_user (kbuf->ctime.tv_sec, &ubuf->st_ctime) || | ||
101 | __put_user (kbuf->ctime.tv_nsec, &ubuf->st_ctime_nsec) || | ||
102 | __put_user (kbuf->blksize, &ubuf->st_blksize) || | ||
103 | __put_user (kbuf->blocks, &ubuf->st_blocks)) | ||
104 | return -EFAULT; | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | asmlinkage long | ||
109 | sys32_truncate64(char __user * filename, unsigned long offset_low, unsigned long offset_high) | ||
110 | { | ||
111 | return sys_truncate(filename, ((loff_t) offset_high << 32) | offset_low); | ||
112 | } | ||
113 | |||
114 | asmlinkage long | ||
115 | sys32_ftruncate64(unsigned int fd, unsigned long offset_low, unsigned long offset_high) | ||
116 | { | ||
117 | return sys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low); | ||
118 | } | ||
119 | |||
120 | /* Another set for IA32/LFS -- x86_64 struct stat is different due to | ||
121 | support for 64bit inode numbers. */ | ||
122 | |||
123 | static int | ||
124 | cp_stat64(struct stat64 __user *ubuf, struct kstat *stat) | ||
125 | { | ||
126 | typeof(ubuf->st_uid) uid = 0; | ||
127 | typeof(ubuf->st_gid) gid = 0; | ||
128 | SET_UID(uid, stat->uid); | ||
129 | SET_GID(gid, stat->gid); | ||
130 | if (!access_ok(VERIFY_WRITE, ubuf, sizeof(struct stat64)) || | ||
131 | __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) || | ||
132 | __put_user (stat->ino, &ubuf->__st_ino) || | ||
133 | __put_user (stat->ino, &ubuf->st_ino) || | ||
134 | __put_user (stat->mode, &ubuf->st_mode) || | ||
135 | __put_user (stat->nlink, &ubuf->st_nlink) || | ||
136 | __put_user (uid, &ubuf->st_uid) || | ||
137 | __put_user (gid, &ubuf->st_gid) || | ||
138 | __put_user (huge_encode_dev(stat->rdev), &ubuf->st_rdev) || | ||
139 | __put_user (stat->size, &ubuf->st_size) || | ||
140 | __put_user (stat->atime.tv_sec, &ubuf->st_atime) || | ||
141 | __put_user (stat->atime.tv_nsec, &ubuf->st_atime_nsec) || | ||
142 | __put_user (stat->mtime.tv_sec, &ubuf->st_mtime) || | ||
143 | __put_user (stat->mtime.tv_nsec, &ubuf->st_mtime_nsec) || | ||
144 | __put_user (stat->ctime.tv_sec, &ubuf->st_ctime) || | ||
145 | __put_user (stat->ctime.tv_nsec, &ubuf->st_ctime_nsec) || | ||
146 | __put_user (stat->blksize, &ubuf->st_blksize) || | ||
147 | __put_user (stat->blocks, &ubuf->st_blocks)) | ||
148 | return -EFAULT; | ||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | asmlinkage long | ||
153 | sys32_stat64(char __user * filename, struct stat64 __user *statbuf) | ||
154 | { | ||
155 | struct kstat stat; | ||
156 | int ret = vfs_stat(filename, &stat); | ||
157 | if (!ret) | ||
158 | ret = cp_stat64(statbuf, &stat); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | asmlinkage long | ||
163 | sys32_lstat64(char __user * filename, struct stat64 __user *statbuf) | ||
164 | { | ||
165 | struct kstat stat; | ||
166 | int ret = vfs_lstat(filename, &stat); | ||
167 | if (!ret) | ||
168 | ret = cp_stat64(statbuf, &stat); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | asmlinkage long | ||
173 | sys32_fstat64(unsigned int fd, struct stat64 __user *statbuf) | ||
174 | { | ||
175 | struct kstat stat; | ||
176 | int ret = vfs_fstat(fd, &stat); | ||
177 | if (!ret) | ||
178 | ret = cp_stat64(statbuf, &stat); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Linux/i386 didn't use to be able to handle more than | ||
184 | * 4 system call parameters, so these system calls used a memory | ||
185 | * block for parameter passing.. | ||
186 | */ | ||
187 | |||
188 | struct mmap_arg_struct { | ||
189 | unsigned int addr; | ||
190 | unsigned int len; | ||
191 | unsigned int prot; | ||
192 | unsigned int flags; | ||
193 | unsigned int fd; | ||
194 | unsigned int offset; | ||
195 | }; | ||
196 | |||
197 | asmlinkage long | ||
198 | sys32_mmap(struct mmap_arg_struct __user *arg) | ||
199 | { | ||
200 | struct mmap_arg_struct a; | ||
201 | struct file *file = NULL; | ||
202 | unsigned long retval; | ||
203 | struct mm_struct *mm ; | ||
204 | |||
205 | if (copy_from_user(&a, arg, sizeof(a))) | ||
206 | return -EFAULT; | ||
207 | |||
208 | if (a.offset & ~PAGE_MASK) | ||
209 | return -EINVAL; | ||
210 | |||
211 | if (!(a.flags & MAP_ANONYMOUS)) { | ||
212 | file = fget(a.fd); | ||
213 | if (!file) | ||
214 | return -EBADF; | ||
215 | } | ||
216 | |||
217 | mm = current->mm; | ||
218 | down_write(&mm->mmap_sem); | ||
219 | retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, a.offset>>PAGE_SHIFT); | ||
220 | if (file) | ||
221 | fput(file); | ||
222 | |||
223 | up_write(&mm->mmap_sem); | ||
224 | |||
225 | return retval; | ||
226 | } | ||
227 | |||
228 | asmlinkage long | ||
229 | sys32_mprotect(unsigned long start, size_t len, unsigned long prot) | ||
230 | { | ||
231 | return sys_mprotect(start,len,prot); | ||
232 | } | ||
233 | |||
234 | asmlinkage long | ||
235 | sys32_pipe(int __user *fd) | ||
236 | { | ||
237 | int retval; | ||
238 | int fds[2]; | ||
239 | |||
240 | retval = do_pipe(fds); | ||
241 | if (retval) | ||
242 | goto out; | ||
243 | if (copy_to_user(fd, fds, sizeof(fds))) | ||
244 | retval = -EFAULT; | ||
245 | out: | ||
246 | return retval; | ||
247 | } | ||
248 | |||
249 | asmlinkage long | ||
250 | sys32_rt_sigaction(int sig, struct sigaction32 __user *act, | ||
251 | struct sigaction32 __user *oact, unsigned int sigsetsize) | ||
252 | { | ||
253 | struct k_sigaction new_ka, old_ka; | ||
254 | int ret; | ||
255 | compat_sigset_t set32; | ||
256 | |||
257 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
258 | if (sigsetsize != sizeof(compat_sigset_t)) | ||
259 | return -EINVAL; | ||
260 | |||
261 | if (act) { | ||
262 | compat_uptr_t handler, restorer; | ||
263 | |||
264 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
265 | __get_user(handler, &act->sa_handler) || | ||
266 | __get_user(new_ka.sa.sa_flags, &act->sa_flags) || | ||
267 | __get_user(restorer, &act->sa_restorer)|| | ||
268 | __copy_from_user(&set32, &act->sa_mask, sizeof(compat_sigset_t))) | ||
269 | return -EFAULT; | ||
270 | new_ka.sa.sa_handler = compat_ptr(handler); | ||
271 | new_ka.sa.sa_restorer = compat_ptr(restorer); | ||
272 | /* FIXME: here we rely on _COMPAT_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ | ||
273 | switch (_NSIG_WORDS) { | ||
274 | case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] | ||
275 | | (((long)set32.sig[7]) << 32); | ||
276 | case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] | ||
277 | | (((long)set32.sig[5]) << 32); | ||
278 | case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] | ||
279 | | (((long)set32.sig[3]) << 32); | ||
280 | case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] | ||
281 | | (((long)set32.sig[1]) << 32); | ||
282 | } | ||
283 | } | ||
284 | |||
285 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
286 | |||
287 | if (!ret && oact) { | ||
288 | /* FIXME: here we rely on _COMPAT_NSIG_WORS to be >= than _NSIG_WORDS << 1 */ | ||
289 | switch (_NSIG_WORDS) { | ||
290 | case 4: | ||
291 | set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); | ||
292 | set32.sig[6] = old_ka.sa.sa_mask.sig[3]; | ||
293 | case 3: | ||
294 | set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); | ||
295 | set32.sig[4] = old_ka.sa.sa_mask.sig[2]; | ||
296 | case 2: | ||
297 | set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); | ||
298 | set32.sig[2] = old_ka.sa.sa_mask.sig[1]; | ||
299 | case 1: | ||
300 | set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); | ||
301 | set32.sig[0] = old_ka.sa.sa_mask.sig[0]; | ||
302 | } | ||
303 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
304 | __put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler) || | ||
305 | __put_user(ptr_to_compat(old_ka.sa.sa_restorer), &oact->sa_restorer) || | ||
306 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || | ||
307 | __copy_to_user(&oact->sa_mask, &set32, sizeof(compat_sigset_t))) | ||
308 | return -EFAULT; | ||
309 | } | ||
310 | |||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | asmlinkage long | ||
315 | sys32_sigaction (int sig, struct old_sigaction32 __user *act, struct old_sigaction32 __user *oact) | ||
316 | { | ||
317 | struct k_sigaction new_ka, old_ka; | ||
318 | int ret; | ||
319 | |||
320 | if (act) { | ||
321 | compat_old_sigset_t mask; | ||
322 | compat_uptr_t handler, restorer; | ||
323 | |||
324 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
325 | __get_user(handler, &act->sa_handler) || | ||
326 | __get_user(new_ka.sa.sa_flags, &act->sa_flags) || | ||
327 | __get_user(restorer, &act->sa_restorer) || | ||
328 | __get_user(mask, &act->sa_mask)) | ||
329 | return -EFAULT; | ||
330 | |||
331 | new_ka.sa.sa_handler = compat_ptr(handler); | ||
332 | new_ka.sa.sa_restorer = compat_ptr(restorer); | ||
333 | |||
334 | siginitset(&new_ka.sa.sa_mask, mask); | ||
335 | } | ||
336 | |||
337 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
338 | |||
339 | if (!ret && oact) { | ||
340 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
341 | __put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler) || | ||
342 | __put_user(ptr_to_compat(old_ka.sa.sa_restorer), &oact->sa_restorer) || | ||
343 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || | ||
344 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) | ||
345 | return -EFAULT; | ||
346 | } | ||
347 | |||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | asmlinkage long | ||
352 | sys32_rt_sigprocmask(int how, compat_sigset_t __user *set, | ||
353 | compat_sigset_t __user *oset, unsigned int sigsetsize) | ||
354 | { | ||
355 | sigset_t s; | ||
356 | compat_sigset_t s32; | ||
357 | int ret; | ||
358 | mm_segment_t old_fs = get_fs(); | ||
359 | |||
360 | if (set) { | ||
361 | if (copy_from_user (&s32, set, sizeof(compat_sigset_t))) | ||
362 | return -EFAULT; | ||
363 | switch (_NSIG_WORDS) { | ||
364 | case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); | ||
365 | case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); | ||
366 | case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); | ||
367 | case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); | ||
368 | } | ||
369 | } | ||
370 | set_fs (KERNEL_DS); | ||
371 | ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, | ||
372 | sigsetsize); | ||
373 | set_fs (old_fs); | ||
374 | if (ret) return ret; | ||
375 | if (oset) { | ||
376 | switch (_NSIG_WORDS) { | ||
377 | case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; | ||
378 | case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; | ||
379 | case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; | ||
380 | case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; | ||
381 | } | ||
382 | if (copy_to_user (oset, &s32, sizeof(compat_sigset_t))) | ||
383 | return -EFAULT; | ||
384 | } | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static inline long | ||
389 | get_tv32(struct timeval *o, struct compat_timeval __user *i) | ||
390 | { | ||
391 | int err = -EFAULT; | ||
392 | if (access_ok(VERIFY_READ, i, sizeof(*i))) { | ||
393 | err = __get_user(o->tv_sec, &i->tv_sec); | ||
394 | err |= __get_user(o->tv_usec, &i->tv_usec); | ||
395 | } | ||
396 | return err; | ||
397 | } | ||
398 | |||
399 | static inline long | ||
400 | put_tv32(struct compat_timeval __user *o, struct timeval *i) | ||
401 | { | ||
402 | int err = -EFAULT; | ||
403 | if (access_ok(VERIFY_WRITE, o, sizeof(*o))) { | ||
404 | err = __put_user(i->tv_sec, &o->tv_sec); | ||
405 | err |= __put_user(i->tv_usec, &o->tv_usec); | ||
406 | } | ||
407 | return err; | ||
408 | } | ||
409 | |||
410 | extern int do_setitimer(int which, struct itimerval *, struct itimerval *); | ||
411 | |||
412 | asmlinkage long | ||
413 | sys32_alarm(unsigned int seconds) | ||
414 | { | ||
415 | struct itimerval it_new, it_old; | ||
416 | unsigned int oldalarm; | ||
417 | |||
418 | it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; | ||
419 | it_new.it_value.tv_sec = seconds; | ||
420 | it_new.it_value.tv_usec = 0; | ||
421 | do_setitimer(ITIMER_REAL, &it_new, &it_old); | ||
422 | oldalarm = it_old.it_value.tv_sec; | ||
423 | /* ehhh.. We can't return 0 if we have an alarm pending.. */ | ||
424 | /* And we'd better return too much than too little anyway */ | ||
425 | if (it_old.it_value.tv_usec) | ||
426 | oldalarm++; | ||
427 | return oldalarm; | ||
428 | } | ||
429 | |||
430 | /* Translations due to time_t size differences. Which affects all | ||
431 | sorts of things, like timeval and itimerval. */ | ||
432 | |||
433 | extern struct timezone sys_tz; | ||
434 | |||
435 | asmlinkage long | ||
436 | sys32_gettimeofday(struct compat_timeval __user *tv, struct timezone __user *tz) | ||
437 | { | ||
438 | if (tv) { | ||
439 | struct timeval ktv; | ||
440 | do_gettimeofday(&ktv); | ||
441 | if (put_tv32(tv, &ktv)) | ||
442 | return -EFAULT; | ||
443 | } | ||
444 | if (tz) { | ||
445 | if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) | ||
446 | return -EFAULT; | ||
447 | } | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | asmlinkage long | ||
452 | sys32_settimeofday(struct compat_timeval __user *tv, struct timezone __user *tz) | ||
453 | { | ||
454 | struct timeval ktv; | ||
455 | struct timespec kts; | ||
456 | struct timezone ktz; | ||
457 | |||
458 | if (tv) { | ||
459 | if (get_tv32(&ktv, tv)) | ||
460 | return -EFAULT; | ||
461 | kts.tv_sec = ktv.tv_sec; | ||
462 | kts.tv_nsec = ktv.tv_usec * NSEC_PER_USEC; | ||
463 | } | ||
464 | if (tz) { | ||
465 | if (copy_from_user(&ktz, tz, sizeof(ktz))) | ||
466 | return -EFAULT; | ||
467 | } | ||
468 | |||
469 | return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); | ||
470 | } | ||
471 | |||
472 | struct sel_arg_struct { | ||
473 | unsigned int n; | ||
474 | unsigned int inp; | ||
475 | unsigned int outp; | ||
476 | unsigned int exp; | ||
477 | unsigned int tvp; | ||
478 | }; | ||
479 | |||
480 | asmlinkage long | ||
481 | sys32_old_select(struct sel_arg_struct __user *arg) | ||
482 | { | ||
483 | struct sel_arg_struct a; | ||
484 | |||
485 | if (copy_from_user(&a, arg, sizeof(a))) | ||
486 | return -EFAULT; | ||
487 | return compat_sys_select(a.n, compat_ptr(a.inp), compat_ptr(a.outp), | ||
488 | compat_ptr(a.exp), compat_ptr(a.tvp)); | ||
489 | } | ||
490 | |||
491 | extern asmlinkage long | ||
492 | compat_sys_wait4(compat_pid_t pid, compat_uint_t * stat_addr, int options, | ||
493 | struct compat_rusage *ru); | ||
494 | |||
495 | asmlinkage long | ||
496 | sys32_waitpid(compat_pid_t pid, unsigned int *stat_addr, int options) | ||
497 | { | ||
498 | return compat_sys_wait4(pid, stat_addr, options, NULL); | ||
499 | } | ||
500 | |||
501 | int sys32_ni_syscall(int call) | ||
502 | { | ||
503 | struct task_struct *me = current; | ||
504 | static char lastcomm[sizeof(me->comm)]; | ||
505 | |||
506 | if (strncmp(lastcomm, me->comm, sizeof(lastcomm))) { | ||
507 | printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", | ||
508 | call, me->comm); | ||
509 | strncpy(lastcomm, me->comm, sizeof(lastcomm)); | ||
510 | } | ||
511 | return -ENOSYS; | ||
512 | } | ||
513 | |||
514 | /* 32-bit timeval and related flotsam. */ | ||
515 | |||
516 | asmlinkage long | ||
517 | sys32_sysfs(int option, u32 arg1, u32 arg2) | ||
518 | { | ||
519 | return sys_sysfs(option, arg1, arg2); | ||
520 | } | ||
521 | |||
522 | struct sysinfo32 { | ||
523 | s32 uptime; | ||
524 | u32 loads[3]; | ||
525 | u32 totalram; | ||
526 | u32 freeram; | ||
527 | u32 sharedram; | ||
528 | u32 bufferram; | ||
529 | u32 totalswap; | ||
530 | u32 freeswap; | ||
531 | unsigned short procs; | ||
532 | unsigned short pad; | ||
533 | u32 totalhigh; | ||
534 | u32 freehigh; | ||
535 | u32 mem_unit; | ||
536 | char _f[20-2*sizeof(u32)-sizeof(int)]; | ||
537 | }; | ||
538 | |||
539 | asmlinkage long | ||
540 | sys32_sysinfo(struct sysinfo32 __user *info) | ||
541 | { | ||
542 | struct sysinfo s; | ||
543 | int ret; | ||
544 | mm_segment_t old_fs = get_fs (); | ||
545 | int bitcount = 0; | ||
546 | |||
547 | set_fs (KERNEL_DS); | ||
548 | ret = sys_sysinfo(&s); | ||
549 | set_fs (old_fs); | ||
550 | |||
551 | /* Check to see if any memory value is too large for 32-bit and scale | ||
552 | * down if needed | ||
553 | */ | ||
554 | if ((s.totalram >> 32) || (s.totalswap >> 32)) { | ||
555 | while (s.mem_unit < PAGE_SIZE) { | ||
556 | s.mem_unit <<= 1; | ||
557 | bitcount++; | ||
558 | } | ||
559 | s.totalram >>= bitcount; | ||
560 | s.freeram >>= bitcount; | ||
561 | s.sharedram >>= bitcount; | ||
562 | s.bufferram >>= bitcount; | ||
563 | s.totalswap >>= bitcount; | ||
564 | s.freeswap >>= bitcount; | ||
565 | s.totalhigh >>= bitcount; | ||
566 | s.freehigh >>= bitcount; | ||
567 | } | ||
568 | |||
569 | if (!access_ok(VERIFY_WRITE, info, sizeof(struct sysinfo32)) || | ||
570 | __put_user (s.uptime, &info->uptime) || | ||
571 | __put_user (s.loads[0], &info->loads[0]) || | ||
572 | __put_user (s.loads[1], &info->loads[1]) || | ||
573 | __put_user (s.loads[2], &info->loads[2]) || | ||
574 | __put_user (s.totalram, &info->totalram) || | ||
575 | __put_user (s.freeram, &info->freeram) || | ||
576 | __put_user (s.sharedram, &info->sharedram) || | ||
577 | __put_user (s.bufferram, &info->bufferram) || | ||
578 | __put_user (s.totalswap, &info->totalswap) || | ||
579 | __put_user (s.freeswap, &info->freeswap) || | ||
580 | __put_user (s.procs, &info->procs) || | ||
581 | __put_user (s.totalhigh, &info->totalhigh) || | ||
582 | __put_user (s.freehigh, &info->freehigh) || | ||
583 | __put_user (s.mem_unit, &info->mem_unit)) | ||
584 | return -EFAULT; | ||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | asmlinkage long | ||
589 | sys32_sched_rr_get_interval(compat_pid_t pid, struct compat_timespec __user *interval) | ||
590 | { | ||
591 | struct timespec t; | ||
592 | int ret; | ||
593 | mm_segment_t old_fs = get_fs (); | ||
594 | |||
595 | set_fs (KERNEL_DS); | ||
596 | ret = sys_sched_rr_get_interval(pid, &t); | ||
597 | set_fs (old_fs); | ||
598 | if (put_compat_timespec(&t, interval)) | ||
599 | return -EFAULT; | ||
600 | return ret; | ||
601 | } | ||
602 | |||
603 | asmlinkage long | ||
604 | sys32_rt_sigpending(compat_sigset_t __user *set, compat_size_t sigsetsize) | ||
605 | { | ||
606 | sigset_t s; | ||
607 | compat_sigset_t s32; | ||
608 | int ret; | ||
609 | mm_segment_t old_fs = get_fs(); | ||
610 | |||
611 | set_fs (KERNEL_DS); | ||
612 | ret = sys_rt_sigpending(&s, sigsetsize); | ||
613 | set_fs (old_fs); | ||
614 | if (!ret) { | ||
615 | switch (_NSIG_WORDS) { | ||
616 | case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; | ||
617 | case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; | ||
618 | case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; | ||
619 | case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; | ||
620 | } | ||
621 | if (copy_to_user (set, &s32, sizeof(compat_sigset_t))) | ||
622 | return -EFAULT; | ||
623 | } | ||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | asmlinkage long | ||
628 | sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo) | ||
629 | { | ||
630 | siginfo_t info; | ||
631 | int ret; | ||
632 | mm_segment_t old_fs = get_fs(); | ||
633 | |||
634 | if (copy_siginfo_from_user32(&info, uinfo)) | ||
635 | return -EFAULT; | ||
636 | set_fs (KERNEL_DS); | ||
637 | ret = sys_rt_sigqueueinfo(pid, sig, &info); | ||
638 | set_fs (old_fs); | ||
639 | return ret; | ||
640 | } | ||
641 | |||
642 | /* These are here just in case some old ia32 binary calls it. */ | ||
643 | asmlinkage long | ||
644 | sys32_pause(void) | ||
645 | { | ||
646 | current->state = TASK_INTERRUPTIBLE; | ||
647 | schedule(); | ||
648 | return -ERESTARTNOHAND; | ||
649 | } | ||
650 | |||
651 | |||
652 | #ifdef CONFIG_SYSCTL | ||
653 | struct sysctl_ia32 { | ||
654 | unsigned int name; | ||
655 | int nlen; | ||
656 | unsigned int oldval; | ||
657 | unsigned int oldlenp; | ||
658 | unsigned int newval; | ||
659 | unsigned int newlen; | ||
660 | unsigned int __unused[4]; | ||
661 | }; | ||
662 | |||
663 | |||
664 | asmlinkage long | ||
665 | sys32_sysctl(struct sysctl_ia32 __user *args32) | ||
666 | { | ||
667 | struct sysctl_ia32 a32; | ||
668 | mm_segment_t old_fs = get_fs (); | ||
669 | void __user *oldvalp, *newvalp; | ||
670 | size_t oldlen; | ||
671 | int __user *namep; | ||
672 | long ret; | ||
673 | extern int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp, | ||
674 | void *newval, size_t newlen); | ||
675 | |||
676 | |||
677 | if (copy_from_user(&a32, args32, sizeof (a32))) | ||
678 | return -EFAULT; | ||
679 | |||
680 | /* | ||
681 | * We need to pre-validate these because we have to disable address checking | ||
682 | * before calling do_sysctl() because of OLDLEN but we can't run the risk of the | ||
683 | * user specifying bad addresses here. Well, since we're dealing with 32 bit | ||
684 | * addresses, we KNOW that access_ok() will always succeed, so this is an | ||
685 | * expensive NOP, but so what... | ||
686 | */ | ||
687 | namep = compat_ptr(a32.name); | ||
688 | oldvalp = compat_ptr(a32.oldval); | ||
689 | newvalp = compat_ptr(a32.newval); | ||
690 | |||
691 | if ((oldvalp && get_user(oldlen, (int __user *)compat_ptr(a32.oldlenp))) | ||
692 | || !access_ok(VERIFY_WRITE, namep, 0) | ||
693 | || !access_ok(VERIFY_WRITE, oldvalp, 0) | ||
694 | || !access_ok(VERIFY_WRITE, newvalp, 0)) | ||
695 | return -EFAULT; | ||
696 | |||
697 | set_fs(KERNEL_DS); | ||
698 | lock_kernel(); | ||
699 | ret = do_sysctl(namep, a32.nlen, oldvalp, &oldlen, newvalp, (size_t) a32.newlen); | ||
700 | unlock_kernel(); | ||
701 | set_fs(old_fs); | ||
702 | |||
703 | if (oldvalp && put_user (oldlen, (int __user *)compat_ptr(a32.oldlenp))) | ||
704 | return -EFAULT; | ||
705 | |||
706 | return ret; | ||
707 | } | ||
708 | #endif | ||
709 | |||
710 | /* warning: next two assume little endian */ | ||
711 | asmlinkage long | ||
712 | sys32_pread(unsigned int fd, char __user *ubuf, u32 count, u32 poslo, u32 poshi) | ||
713 | { | ||
714 | return sys_pread64(fd, ubuf, count, | ||
715 | ((loff_t)AA(poshi) << 32) | AA(poslo)); | ||
716 | } | ||
717 | |||
718 | asmlinkage long | ||
719 | sys32_pwrite(unsigned int fd, char __user *ubuf, u32 count, u32 poslo, u32 poshi) | ||
720 | { | ||
721 | return sys_pwrite64(fd, ubuf, count, | ||
722 | ((loff_t)AA(poshi) << 32) | AA(poslo)); | ||
723 | } | ||
724 | |||
725 | |||
726 | asmlinkage long | ||
727 | sys32_personality(unsigned long personality) | ||
728 | { | ||
729 | int ret; | ||
730 | if (personality(current->personality) == PER_LINUX32 && | ||
731 | personality == PER_LINUX) | ||
732 | personality = PER_LINUX32; | ||
733 | ret = sys_personality(personality); | ||
734 | if (ret == PER_LINUX32) | ||
735 | ret = PER_LINUX; | ||
736 | return ret; | ||
737 | } | ||
738 | |||
739 | asmlinkage long | ||
740 | sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, s32 count) | ||
741 | { | ||
742 | mm_segment_t old_fs = get_fs(); | ||
743 | int ret; | ||
744 | off_t of; | ||
745 | |||
746 | if (offset && get_user(of, offset)) | ||
747 | return -EFAULT; | ||
748 | |||
749 | set_fs(KERNEL_DS); | ||
750 | ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); | ||
751 | set_fs(old_fs); | ||
752 | |||
753 | if (!ret && offset && put_user(of, offset)) | ||
754 | return -EFAULT; | ||
755 | |||
756 | return ret; | ||
757 | } | ||
758 | |||
759 | /* Handle adjtimex compatibility. */ | ||
760 | |||
761 | struct timex32 { | ||
762 | u32 modes; | ||
763 | s32 offset, freq, maxerror, esterror; | ||
764 | s32 status, constant, precision, tolerance; | ||
765 | struct compat_timeval time; | ||
766 | s32 tick; | ||
767 | s32 ppsfreq, jitter, shift, stabil; | ||
768 | s32 jitcnt, calcnt, errcnt, stbcnt; | ||
769 | s32 :32; s32 :32; s32 :32; s32 :32; | ||
770 | s32 :32; s32 :32; s32 :32; s32 :32; | ||
771 | s32 :32; s32 :32; s32 :32; s32 :32; | ||
772 | }; | ||
773 | |||
774 | extern int do_adjtimex(struct timex *); | ||
775 | |||
776 | asmlinkage long | ||
777 | sys32_adjtimex(struct timex32 __user *utp) | ||
778 | { | ||
779 | struct timex txc; | ||
780 | int ret; | ||
781 | |||
782 | memset(&txc, 0, sizeof(struct timex)); | ||
783 | |||
784 | if (!access_ok(VERIFY_READ, utp, sizeof(struct timex32)) || | ||
785 | __get_user(txc.modes, &utp->modes) || | ||
786 | __get_user(txc.offset, &utp->offset) || | ||
787 | __get_user(txc.freq, &utp->freq) || | ||
788 | __get_user(txc.maxerror, &utp->maxerror) || | ||
789 | __get_user(txc.esterror, &utp->esterror) || | ||
790 | __get_user(txc.status, &utp->status) || | ||
791 | __get_user(txc.constant, &utp->constant) || | ||
792 | __get_user(txc.precision, &utp->precision) || | ||
793 | __get_user(txc.tolerance, &utp->tolerance) || | ||
794 | __get_user(txc.time.tv_sec, &utp->time.tv_sec) || | ||
795 | __get_user(txc.time.tv_usec, &utp->time.tv_usec) || | ||
796 | __get_user(txc.tick, &utp->tick) || | ||
797 | __get_user(txc.ppsfreq, &utp->ppsfreq) || | ||
798 | __get_user(txc.jitter, &utp->jitter) || | ||
799 | __get_user(txc.shift, &utp->shift) || | ||
800 | __get_user(txc.stabil, &utp->stabil) || | ||
801 | __get_user(txc.jitcnt, &utp->jitcnt) || | ||
802 | __get_user(txc.calcnt, &utp->calcnt) || | ||
803 | __get_user(txc.errcnt, &utp->errcnt) || | ||
804 | __get_user(txc.stbcnt, &utp->stbcnt)) | ||
805 | return -EFAULT; | ||
806 | |||
807 | ret = do_adjtimex(&txc); | ||
808 | |||
809 | if (!access_ok(VERIFY_WRITE, utp, sizeof(struct timex32)) || | ||
810 | __put_user(txc.modes, &utp->modes) || | ||
811 | __put_user(txc.offset, &utp->offset) || | ||
812 | __put_user(txc.freq, &utp->freq) || | ||
813 | __put_user(txc.maxerror, &utp->maxerror) || | ||
814 | __put_user(txc.esterror, &utp->esterror) || | ||
815 | __put_user(txc.status, &utp->status) || | ||
816 | __put_user(txc.constant, &utp->constant) || | ||
817 | __put_user(txc.precision, &utp->precision) || | ||
818 | __put_user(txc.tolerance, &utp->tolerance) || | ||
819 | __put_user(txc.time.tv_sec, &utp->time.tv_sec) || | ||
820 | __put_user(txc.time.tv_usec, &utp->time.tv_usec) || | ||
821 | __put_user(txc.tick, &utp->tick) || | ||
822 | __put_user(txc.ppsfreq, &utp->ppsfreq) || | ||
823 | __put_user(txc.jitter, &utp->jitter) || | ||
824 | __put_user(txc.shift, &utp->shift) || | ||
825 | __put_user(txc.stabil, &utp->stabil) || | ||
826 | __put_user(txc.jitcnt, &utp->jitcnt) || | ||
827 | __put_user(txc.calcnt, &utp->calcnt) || | ||
828 | __put_user(txc.errcnt, &utp->errcnt) || | ||
829 | __put_user(txc.stbcnt, &utp->stbcnt)) | ||
830 | ret = -EFAULT; | ||
831 | |||
832 | return ret; | ||
833 | } | ||
834 | |||
835 | asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len, | ||
836 | unsigned long prot, unsigned long flags, | ||
837 | unsigned long fd, unsigned long pgoff) | ||
838 | { | ||
839 | struct mm_struct *mm = current->mm; | ||
840 | unsigned long error; | ||
841 | struct file * file = NULL; | ||
842 | |||
843 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
844 | if (!(flags & MAP_ANONYMOUS)) { | ||
845 | file = fget(fd); | ||
846 | if (!file) | ||
847 | return -EBADF; | ||
848 | } | ||
849 | |||
850 | down_write(&mm->mmap_sem); | ||
851 | error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); | ||
852 | up_write(&mm->mmap_sem); | ||
853 | |||
854 | if (file) | ||
855 | fput(file); | ||
856 | return error; | ||
857 | } | ||
858 | |||
859 | asmlinkage long sys32_olduname(struct oldold_utsname __user * name) | ||
860 | { | ||
861 | int error; | ||
862 | |||
863 | if (!name) | ||
864 | return -EFAULT; | ||
865 | if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) | ||
866 | return -EFAULT; | ||
867 | |||
868 | down_read(&uts_sem); | ||
869 | |||
870 | error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); | ||
871 | __put_user(0,name->sysname+__OLD_UTS_LEN); | ||
872 | __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); | ||
873 | __put_user(0,name->nodename+__OLD_UTS_LEN); | ||
874 | __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); | ||
875 | __put_user(0,name->release+__OLD_UTS_LEN); | ||
876 | __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); | ||
877 | __put_user(0,name->version+__OLD_UTS_LEN); | ||
878 | { | ||
879 | char *arch = "x86_64"; | ||
880 | if (personality(current->personality) == PER_LINUX32) | ||
881 | arch = "i686"; | ||
882 | |||
883 | __copy_to_user(&name->machine,arch,strlen(arch)+1); | ||
884 | } | ||
885 | |||
886 | up_read(&uts_sem); | ||
887 | |||
888 | error = error ? -EFAULT : 0; | ||
889 | |||
890 | return error; | ||
891 | } | ||
892 | |||
893 | long sys32_uname(struct old_utsname __user * name) | ||
894 | { | ||
895 | int err; | ||
896 | if (!name) | ||
897 | return -EFAULT; | ||
898 | down_read(&uts_sem); | ||
899 | err=copy_to_user(name, &system_utsname, sizeof (*name)); | ||
900 | up_read(&uts_sem); | ||
901 | if (personality(current->personality) == PER_LINUX32) | ||
902 | err |= copy_to_user(&name->machine, "i686", 5); | ||
903 | return err?-EFAULT:0; | ||
904 | } | ||
905 | |||
906 | long sys32_ustat(unsigned dev, struct ustat32 __user *u32p) | ||
907 | { | ||
908 | struct ustat u; | ||
909 | mm_segment_t seg; | ||
910 | int ret; | ||
911 | |||
912 | seg = get_fs(); | ||
913 | set_fs(KERNEL_DS); | ||
914 | ret = sys_ustat(dev,&u); | ||
915 | set_fs(seg); | ||
916 | if (ret >= 0) { | ||
917 | if (!access_ok(VERIFY_WRITE,u32p,sizeof(struct ustat32)) || | ||
918 | __put_user((__u32) u.f_tfree, &u32p->f_tfree) || | ||
919 | __put_user((__u32) u.f_tinode, &u32p->f_tfree) || | ||
920 | __copy_to_user(&u32p->f_fname, u.f_fname, sizeof(u.f_fname)) || | ||
921 | __copy_to_user(&u32p->f_fpack, u.f_fpack, sizeof(u.f_fpack))) | ||
922 | ret = -EFAULT; | ||
923 | } | ||
924 | return ret; | ||
925 | } | ||
926 | |||
927 | asmlinkage long sys32_execve(char __user *name, compat_uptr_t __user *argv, | ||
928 | compat_uptr_t __user *envp, struct pt_regs *regs) | ||
929 | { | ||
930 | long error; | ||
931 | char * filename; | ||
932 | |||
933 | filename = getname(name); | ||
934 | error = PTR_ERR(filename); | ||
935 | if (IS_ERR(filename)) | ||
936 | return error; | ||
937 | error = compat_do_execve(filename, argv, envp, regs); | ||
938 | if (error == 0) { | ||
939 | task_lock(current); | ||
940 | current->ptrace &= ~PT_DTRACE; | ||
941 | task_unlock(current); | ||
942 | } | ||
943 | putname(filename); | ||
944 | return error; | ||
945 | } | ||
946 | |||
947 | asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp, | ||
948 | struct pt_regs *regs) | ||
949 | { | ||
950 | void __user *parent_tid = (void __user *)regs->rdx; | ||
951 | void __user *child_tid = (void __user *)regs->rdi; | ||
952 | if (!newsp) | ||
953 | newsp = regs->rsp; | ||
954 | return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); | ||
955 | } | ||
956 | |||
957 | /* | ||
958 | * Some system calls that need sign extended arguments. This could be done by a generic wrapper. | ||
959 | */ | ||
960 | |||
961 | long sys32_lseek (unsigned int fd, int offset, unsigned int whence) | ||
962 | { | ||
963 | return sys_lseek(fd, offset, whence); | ||
964 | } | ||
965 | |||
966 | long sys32_kill(int pid, int sig) | ||
967 | { | ||
968 | return sys_kill(pid, sig); | ||
969 | } | ||
970 | |||
971 | asmlinkage long sys32_open(const char __user * filename, int flags, int mode) | ||
972 | { | ||
973 | char * tmp; | ||
974 | int fd, error; | ||
975 | |||
976 | /* don't force O_LARGEFILE */ | ||
977 | tmp = getname(filename); | ||
978 | fd = PTR_ERR(tmp); | ||
979 | if (!IS_ERR(tmp)) { | ||
980 | fd = get_unused_fd(); | ||
981 | if (fd >= 0) { | ||
982 | struct file *f = filp_open(tmp, flags, mode); | ||
983 | error = PTR_ERR(f); | ||
984 | if (IS_ERR(f)) { | ||
985 | put_unused_fd(fd); | ||
986 | fd = error; | ||
987 | } else | ||
988 | fd_install(fd, f); | ||
989 | } | ||
990 | putname(tmp); | ||
991 | } | ||
992 | return fd; | ||
993 | } | ||
994 | |||
995 | extern asmlinkage long | ||
996 | sys_timer_create(clockid_t which_clock, | ||
997 | struct sigevent __user *timer_event_spec, | ||
998 | timer_t __user * created_timer_id); | ||
999 | |||
1000 | long | ||
1001 | sys32_timer_create(u32 clock, struct compat_sigevent __user *se32, timer_t __user *timer_id) | ||
1002 | { | ||
1003 | struct sigevent __user *p = NULL; | ||
1004 | if (se32) { | ||
1005 | struct sigevent se; | ||
1006 | p = compat_alloc_user_space(sizeof(struct sigevent)); | ||
1007 | if (get_compat_sigevent(&se, se32) || | ||
1008 | copy_to_user(p, &se, sizeof(se))) | ||
1009 | return -EFAULT; | ||
1010 | } | ||
1011 | return sys_timer_create(clock, p, timer_id); | ||
1012 | } | ||
1013 | |||
1014 | long sys32_fadvise64_64(int fd, __u32 offset_low, __u32 offset_high, | ||
1015 | __u32 len_low, __u32 len_high, int advice) | ||
1016 | { | ||
1017 | return sys_fadvise64_64(fd, | ||
1018 | (((u64)offset_high)<<32) | offset_low, | ||
1019 | (((u64)len_high)<<32) | len_low, | ||
1020 | advice); | ||
1021 | } | ||
1022 | |||
1023 | long sys32_vm86_warning(void) | ||
1024 | { | ||
1025 | struct task_struct *me = current; | ||
1026 | static char lastcomm[sizeof(me->comm)]; | ||
1027 | if (strncmp(lastcomm, me->comm, sizeof(lastcomm))) { | ||
1028 | printk(KERN_INFO "%s: vm86 mode not supported on 64 bit kernel\n", | ||
1029 | me->comm); | ||
1030 | strncpy(lastcomm, me->comm, sizeof(lastcomm)); | ||
1031 | } | ||
1032 | return -ENOSYS; | ||
1033 | } | ||
1034 | |||
1035 | long sys32_lookup_dcookie(u32 addr_low, u32 addr_high, | ||
1036 | char __user * buf, size_t len) | ||
1037 | { | ||
1038 | return sys_lookup_dcookie(((u64)addr_high << 32) | addr_low, buf, len); | ||
1039 | } | ||
1040 | |||
1041 | static int __init ia32_init (void) | ||
1042 | { | ||
1043 | printk("IA32 emulation $Id: sys_ia32.c,v 1.32 2002/03/24 13:02:28 ak Exp $\n"); | ||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | __initcall(ia32_init); | ||
1048 | |||
1049 | extern unsigned long ia32_sys_call_table[]; | ||
1050 | EXPORT_SYMBOL(ia32_sys_call_table); | ||
diff --git a/arch/x86_64/ia32/syscall32.c b/arch/x86_64/ia32/syscall32.c new file mode 100644 index 000000000000..399ff4985099 --- /dev/null +++ b/arch/x86_64/ia32/syscall32.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* Copyright 2002,2003 Andi Kleen, SuSE Labs */ | ||
2 | |||
3 | /* vsyscall handling for 32bit processes. Map a stub page into it | ||
4 | on demand because 32bit cannot reach the kernel's fixmaps */ | ||
5 | |||
6 | #include <linux/mm.h> | ||
7 | #include <linux/string.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/gfp.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/stringify.h> | ||
12 | #include <asm/proto.h> | ||
13 | #include <asm/tlbflush.h> | ||
14 | #include <asm/ia32_unistd.h> | ||
15 | |||
16 | /* 32bit VDSOs mapped into user space. */ | ||
17 | asm(".section \".init.data\",\"aw\"\n" | ||
18 | "syscall32_syscall:\n" | ||
19 | ".incbin \"arch/x86_64/ia32/vsyscall-syscall.so\"\n" | ||
20 | "syscall32_syscall_end:\n" | ||
21 | "syscall32_sysenter:\n" | ||
22 | ".incbin \"arch/x86_64/ia32/vsyscall-sysenter.so\"\n" | ||
23 | "syscall32_sysenter_end:\n" | ||
24 | ".previous"); | ||
25 | |||
26 | extern unsigned char syscall32_syscall[], syscall32_syscall_end[]; | ||
27 | extern unsigned char syscall32_sysenter[], syscall32_sysenter_end[]; | ||
28 | extern int sysctl_vsyscall32; | ||
29 | |||
30 | char *syscall32_page; | ||
31 | static int use_sysenter = -1; | ||
32 | |||
33 | /* | ||
34 | * Map the 32bit vsyscall page on demand. | ||
35 | * | ||
36 | * RED-PEN: This knows too much about high level VM. | ||
37 | * | ||
38 | * Alternative would be to generate a vma with appropriate backing options | ||
39 | * and let it be handled by generic VM. | ||
40 | */ | ||
41 | int __map_syscall32(struct mm_struct *mm, unsigned long address) | ||
42 | { | ||
43 | pgd_t *pgd; | ||
44 | pud_t *pud; | ||
45 | pte_t *pte; | ||
46 | pmd_t *pmd; | ||
47 | int err = -ENOMEM; | ||
48 | |||
49 | spin_lock(&mm->page_table_lock); | ||
50 | pgd = pgd_offset(mm, address); | ||
51 | pud = pud_alloc(mm, pgd, address); | ||
52 | if (pud) { | ||
53 | pmd = pmd_alloc(mm, pud, address); | ||
54 | if (pmd && (pte = pte_alloc_map(mm, pmd, address)) != NULL) { | ||
55 | if (pte_none(*pte)) { | ||
56 | set_pte(pte, | ||
57 | mk_pte(virt_to_page(syscall32_page), | ||
58 | PAGE_KERNEL_VSYSCALL32)); | ||
59 | } | ||
60 | /* Flush only the local CPU. Other CPUs taking a fault | ||
61 | will just end up here again | ||
62 | This probably not needed and just paranoia. */ | ||
63 | __flush_tlb_one(address); | ||
64 | err = 0; | ||
65 | } | ||
66 | } | ||
67 | spin_unlock(&mm->page_table_lock); | ||
68 | return err; | ||
69 | } | ||
70 | |||
71 | int map_syscall32(struct mm_struct *mm, unsigned long address) | ||
72 | { | ||
73 | int err; | ||
74 | down_read(&mm->mmap_sem); | ||
75 | err = __map_syscall32(mm, address); | ||
76 | up_read(&mm->mmap_sem); | ||
77 | return err; | ||
78 | } | ||
79 | |||
80 | static int __init init_syscall32(void) | ||
81 | { | ||
82 | syscall32_page = (void *)get_zeroed_page(GFP_KERNEL); | ||
83 | if (!syscall32_page) | ||
84 | panic("Cannot allocate syscall32 page"); | ||
85 | SetPageReserved(virt_to_page(syscall32_page)); | ||
86 | if (use_sysenter > 0) { | ||
87 | memcpy(syscall32_page, syscall32_sysenter, | ||
88 | syscall32_sysenter_end - syscall32_sysenter); | ||
89 | } else { | ||
90 | memcpy(syscall32_page, syscall32_syscall, | ||
91 | syscall32_syscall_end - syscall32_syscall); | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | __initcall(init_syscall32); | ||
97 | |||
98 | /* May not be __init: called during resume */ | ||
99 | void syscall32_cpu_init(void) | ||
100 | { | ||
101 | if (use_sysenter < 0) | ||
102 | use_sysenter = (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL); | ||
103 | |||
104 | /* Load these always in case some future AMD CPU supports | ||
105 | SYSENTER from compat mode too. */ | ||
106 | checking_wrmsrl(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS); | ||
107 | checking_wrmsrl(MSR_IA32_SYSENTER_ESP, 0ULL); | ||
108 | checking_wrmsrl(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target); | ||
109 | |||
110 | wrmsrl(MSR_CSTAR, ia32_cstar_target); | ||
111 | } | ||
diff --git a/arch/x86_64/ia32/tls32.c b/arch/x86_64/ia32/tls32.c new file mode 100644 index 000000000000..1cc4340de3ca --- /dev/null +++ b/arch/x86_64/ia32/tls32.c | |||
@@ -0,0 +1,163 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/errno.h> | ||
3 | #include <linux/sched.h> | ||
4 | #include <linux/user.h> | ||
5 | |||
6 | #include <asm/uaccess.h> | ||
7 | #include <asm/desc.h> | ||
8 | #include <asm/system.h> | ||
9 | #include <asm/ldt.h> | ||
10 | #include <asm/processor.h> | ||
11 | #include <asm/proto.h> | ||
12 | |||
13 | /* | ||
14 | * sys_alloc_thread_area: get a yet unused TLS descriptor index. | ||
15 | */ | ||
16 | static int get_free_idx(void) | ||
17 | { | ||
18 | struct thread_struct *t = ¤t->thread; | ||
19 | int idx; | ||
20 | |||
21 | for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) | ||
22 | if (desc_empty((struct n_desc_struct *)(t->tls_array) + idx)) | ||
23 | return idx + GDT_ENTRY_TLS_MIN; | ||
24 | return -ESRCH; | ||
25 | } | ||
26 | |||
27 | /* | ||
28 | * Set a given TLS descriptor: | ||
29 | * When you want addresses > 32bit use arch_prctl() | ||
30 | */ | ||
31 | int do_set_thread_area(struct thread_struct *t, struct user_desc __user *u_info) | ||
32 | { | ||
33 | struct user_desc info; | ||
34 | struct n_desc_struct *desc; | ||
35 | int cpu, idx; | ||
36 | |||
37 | if (copy_from_user(&info, u_info, sizeof(info))) | ||
38 | return -EFAULT; | ||
39 | |||
40 | idx = info.entry_number; | ||
41 | |||
42 | /* | ||
43 | * index -1 means the kernel should try to find and | ||
44 | * allocate an empty descriptor: | ||
45 | */ | ||
46 | if (idx == -1) { | ||
47 | idx = get_free_idx(); | ||
48 | if (idx < 0) | ||
49 | return idx; | ||
50 | if (put_user(idx, &u_info->entry_number)) | ||
51 | return -EFAULT; | ||
52 | } | ||
53 | |||
54 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
55 | return -EINVAL; | ||
56 | |||
57 | desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; | ||
58 | |||
59 | /* | ||
60 | * We must not get preempted while modifying the TLS. | ||
61 | */ | ||
62 | cpu = get_cpu(); | ||
63 | |||
64 | if (LDT_empty(&info)) { | ||
65 | desc->a = 0; | ||
66 | desc->b = 0; | ||
67 | } else { | ||
68 | desc->a = LDT_entry_a(&info); | ||
69 | desc->b = LDT_entry_b(&info); | ||
70 | } | ||
71 | if (t == ¤t->thread) | ||
72 | load_TLS(t, cpu); | ||
73 | |||
74 | put_cpu(); | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | asmlinkage long sys32_set_thread_area(struct user_desc __user *u_info) | ||
79 | { | ||
80 | return do_set_thread_area(¤t->thread, u_info); | ||
81 | } | ||
82 | |||
83 | |||
84 | /* | ||
85 | * Get the current Thread-Local Storage area: | ||
86 | */ | ||
87 | |||
88 | #define GET_BASE(desc) ( \ | ||
89 | (((desc)->a >> 16) & 0x0000ffff) | \ | ||
90 | (((desc)->b << 16) & 0x00ff0000) | \ | ||
91 | ( (desc)->b & 0xff000000) ) | ||
92 | |||
93 | #define GET_LIMIT(desc) ( \ | ||
94 | ((desc)->a & 0x0ffff) | \ | ||
95 | ((desc)->b & 0xf0000) ) | ||
96 | |||
97 | #define GET_32BIT(desc) (((desc)->b >> 22) & 1) | ||
98 | #define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) | ||
99 | #define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) | ||
100 | #define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) | ||
101 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) | ||
102 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) | ||
103 | #define GET_LONGMODE(desc) (((desc)->b >> 21) & 1) | ||
104 | |||
105 | int do_get_thread_area(struct thread_struct *t, struct user_desc __user *u_info) | ||
106 | { | ||
107 | struct user_desc info; | ||
108 | struct n_desc_struct *desc; | ||
109 | int idx; | ||
110 | |||
111 | if (get_user(idx, &u_info->entry_number)) | ||
112 | return -EFAULT; | ||
113 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
114 | return -EINVAL; | ||
115 | |||
116 | desc = ((struct n_desc_struct *)t->tls_array) + idx - GDT_ENTRY_TLS_MIN; | ||
117 | |||
118 | memset(&info, 0, sizeof(struct user_desc)); | ||
119 | info.entry_number = idx; | ||
120 | info.base_addr = GET_BASE(desc); | ||
121 | info.limit = GET_LIMIT(desc); | ||
122 | info.seg_32bit = GET_32BIT(desc); | ||
123 | info.contents = GET_CONTENTS(desc); | ||
124 | info.read_exec_only = !GET_WRITABLE(desc); | ||
125 | info.limit_in_pages = GET_LIMIT_PAGES(desc); | ||
126 | info.seg_not_present = !GET_PRESENT(desc); | ||
127 | info.useable = GET_USEABLE(desc); | ||
128 | info.lm = GET_LONGMODE(desc); | ||
129 | |||
130 | if (copy_to_user(u_info, &info, sizeof(info))) | ||
131 | return -EFAULT; | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | asmlinkage long sys32_get_thread_area(struct user_desc __user *u_info) | ||
136 | { | ||
137 | return do_get_thread_area(¤t->thread, u_info); | ||
138 | } | ||
139 | |||
140 | |||
141 | int ia32_child_tls(struct task_struct *p, struct pt_regs *childregs) | ||
142 | { | ||
143 | struct n_desc_struct *desc; | ||
144 | struct user_desc info; | ||
145 | struct user_desc __user *cp; | ||
146 | int idx; | ||
147 | |||
148 | cp = (void __user *)childregs->rsi; | ||
149 | if (copy_from_user(&info, cp, sizeof(info))) | ||
150 | return -EFAULT; | ||
151 | if (LDT_empty(&info)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | idx = info.entry_number; | ||
155 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
156 | return -EINVAL; | ||
157 | |||
158 | desc = (struct n_desc_struct *)(p->thread.tls_array) + idx - GDT_ENTRY_TLS_MIN; | ||
159 | desc->a = LDT_entry_a(&info); | ||
160 | desc->b = LDT_entry_b(&info); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
diff --git a/arch/x86_64/ia32/vsyscall-sigreturn.S b/arch/x86_64/ia32/vsyscall-sigreturn.S new file mode 100644 index 000000000000..ba4067d350e4 --- /dev/null +++ b/arch/x86_64/ia32/vsyscall-sigreturn.S | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * Common code for the sigreturn entry points on the vsyscall page. | ||
3 | * This code uses SYSCALL_ENTER_KERNEL (either syscall or int $0x80) | ||
4 | * to enter the kernel. | ||
5 | * This file is #include'd by vsyscall-*.S to define them after the | ||
6 | * vsyscall entry point. The addresses we get for these entry points | ||
7 | * by doing ".balign 32" must match in both versions of the page. | ||
8 | */ | ||
9 | |||
10 | .section .text.sigreturn,"ax" | ||
11 | .balign 32 | ||
12 | .globl __kernel_sigreturn | ||
13 | .type __kernel_sigreturn,@function | ||
14 | __kernel_sigreturn: | ||
15 | .LSTART_sigreturn: | ||
16 | popl %eax | ||
17 | movl $__NR_ia32_sigreturn, %eax | ||
18 | SYSCALL_ENTER_KERNEL | ||
19 | .LEND_sigreturn: | ||
20 | .size __kernel_sigreturn,.-.LSTART_sigreturn | ||
21 | |||
22 | .section .text.rtsigreturn,"ax" | ||
23 | .balign 32 | ||
24 | .globl __kernel_rt_sigreturn | ||
25 | .type __kernel_rt_sigreturn,@function | ||
26 | __kernel_rt_sigreturn: | ||
27 | .LSTART_rt_sigreturn: | ||
28 | movl $__NR_ia32_rt_sigreturn, %eax | ||
29 | SYSCALL_ENTER_KERNEL | ||
30 | .LEND_rt_sigreturn: | ||
31 | .size __kernel_rt_sigreturn,.-.LSTART_rt_sigreturn | ||
32 | |||
33 | .section .eh_frame,"a",@progbits | ||
34 | .long .LENDFDE2-.LSTARTFDE2 /* Length FDE */ | ||
35 | .LSTARTFDE2: | ||
36 | .long .LSTARTFDE2-.LSTARTFRAME /* CIE pointer */ | ||
37 | /* HACK: The dwarf2 unwind routines will subtract 1 from the | ||
38 | return address to get an address in the middle of the | ||
39 | presumed call instruction. Since we didn't get here via | ||
40 | a call, we need to include the nop before the real start | ||
41 | to make up for it. */ | ||
42 | .long .LSTART_sigreturn-1-. /* PC-relative start address */ | ||
43 | .long .LEND_sigreturn-.LSTART_sigreturn+1 | ||
44 | .uleb128 0 /* Augmentation length */ | ||
45 | /* What follows are the instructions for the table generation. | ||
46 | We record the locations of each register saved. This is | ||
47 | complicated by the fact that the "CFA" is always assumed to | ||
48 | be the value of the stack pointer in the caller. This means | ||
49 | that we must define the CFA of this body of code to be the | ||
50 | saved value of the stack pointer in the sigcontext. Which | ||
51 | also means that there is no fixed relation to the other | ||
52 | saved registers, which means that we must use DW_CFA_expression | ||
53 | to compute their addresses. It also means that when we | ||
54 | adjust the stack with the popl, we have to do it all over again. */ | ||
55 | |||
56 | #define do_cfa_expr(offset) \ | ||
57 | .byte 0x0f; /* DW_CFA_def_cfa_expression */ \ | ||
58 | .uleb128 1f-0f; /* length */ \ | ||
59 | 0: .byte 0x74; /* DW_OP_breg4 */ \ | ||
60 | .sleb128 offset; /* offset */ \ | ||
61 | .byte 0x06; /* DW_OP_deref */ \ | ||
62 | 1: | ||
63 | |||
64 | #define do_expr(regno, offset) \ | ||
65 | .byte 0x10; /* DW_CFA_expression */ \ | ||
66 | .uleb128 regno; /* regno */ \ | ||
67 | .uleb128 1f-0f; /* length */ \ | ||
68 | 0: .byte 0x74; /* DW_OP_breg4 */ \ | ||
69 | .sleb128 offset; /* offset */ \ | ||
70 | 1: | ||
71 | |||
72 | do_cfa_expr(IA32_SIGCONTEXT_esp+4) | ||
73 | do_expr(0, IA32_SIGCONTEXT_eax+4) | ||
74 | do_expr(1, IA32_SIGCONTEXT_ecx+4) | ||
75 | do_expr(2, IA32_SIGCONTEXT_edx+4) | ||
76 | do_expr(3, IA32_SIGCONTEXT_ebx+4) | ||
77 | do_expr(5, IA32_SIGCONTEXT_ebp+4) | ||
78 | do_expr(6, IA32_SIGCONTEXT_esi+4) | ||
79 | do_expr(7, IA32_SIGCONTEXT_edi+4) | ||
80 | do_expr(8, IA32_SIGCONTEXT_eip+4) | ||
81 | |||
82 | .byte 0x42 /* DW_CFA_advance_loc 2 -- nop; popl eax. */ | ||
83 | |||
84 | do_cfa_expr(IA32_SIGCONTEXT_esp) | ||
85 | do_expr(0, IA32_SIGCONTEXT_eax) | ||
86 | do_expr(1, IA32_SIGCONTEXT_ecx) | ||
87 | do_expr(2, IA32_SIGCONTEXT_edx) | ||
88 | do_expr(3, IA32_SIGCONTEXT_ebx) | ||
89 | do_expr(5, IA32_SIGCONTEXT_ebp) | ||
90 | do_expr(6, IA32_SIGCONTEXT_esi) | ||
91 | do_expr(7, IA32_SIGCONTEXT_edi) | ||
92 | do_expr(8, IA32_SIGCONTEXT_eip) | ||
93 | |||
94 | .align 4 | ||
95 | .LENDFDE2: | ||
96 | |||
97 | .long .LENDFDE3-.LSTARTFDE3 /* Length FDE */ | ||
98 | .LSTARTFDE3: | ||
99 | .long .LSTARTFDE3-.LSTARTFRAME /* CIE pointer */ | ||
100 | /* HACK: See above wrt unwind library assumptions. */ | ||
101 | .long .LSTART_rt_sigreturn-1-. /* PC-relative start address */ | ||
102 | .long .LEND_rt_sigreturn-.LSTART_rt_sigreturn+1 | ||
103 | .uleb128 0 /* Augmentation */ | ||
104 | /* What follows are the instructions for the table generation. | ||
105 | We record the locations of each register saved. This is | ||
106 | slightly less complicated than the above, since we don't | ||
107 | modify the stack pointer in the process. */ | ||
108 | |||
109 | do_cfa_expr(IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_esp) | ||
110 | do_expr(0, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_eax) | ||
111 | do_expr(1, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_ecx) | ||
112 | do_expr(2, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_edx) | ||
113 | do_expr(3, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_ebx) | ||
114 | do_expr(5, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_ebp) | ||
115 | do_expr(6, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_esi) | ||
116 | do_expr(7, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_edi) | ||
117 | do_expr(8, IA32_RT_SIGFRAME_sigcontext-4 + IA32_SIGCONTEXT_eip) | ||
118 | |||
119 | .align 4 | ||
120 | .LENDFDE3: | ||
diff --git a/arch/x86_64/ia32/vsyscall-syscall.S b/arch/x86_64/ia32/vsyscall-syscall.S new file mode 100644 index 000000000000..e2aaf3de8a42 --- /dev/null +++ b/arch/x86_64/ia32/vsyscall-syscall.S | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Code for the vsyscall page. This version uses the syscall instruction. | ||
3 | */ | ||
4 | |||
5 | #include <asm/ia32_unistd.h> | ||
6 | #include <asm/offset.h> | ||
7 | #include <asm/segment.h> | ||
8 | |||
9 | .text | ||
10 | .section .text.vsyscall,"ax" | ||
11 | .globl __kernel_vsyscall | ||
12 | .type __kernel_vsyscall,@function | ||
13 | __kernel_vsyscall: | ||
14 | .LSTART_vsyscall: | ||
15 | push %ebp | ||
16 | .Lpush_ebp: | ||
17 | movl %ecx, %ebp | ||
18 | syscall | ||
19 | movl $__USER32_DS, %ecx | ||
20 | movl %ecx, %ss | ||
21 | movl %ebp, %ecx | ||
22 | popl %ebp | ||
23 | .Lpop_ebp: | ||
24 | ret | ||
25 | .LEND_vsyscall: | ||
26 | .size __kernel_vsyscall,.-.LSTART_vsyscall | ||
27 | |||
28 | .section .eh_frame,"a",@progbits | ||
29 | .LSTARTFRAME: | ||
30 | .long .LENDCIE-.LSTARTCIE | ||
31 | .LSTARTCIE: | ||
32 | .long 0 /* CIE ID */ | ||
33 | .byte 1 /* Version number */ | ||
34 | .string "zR" /* NUL-terminated augmentation string */ | ||
35 | .uleb128 1 /* Code alignment factor */ | ||
36 | .sleb128 -4 /* Data alignment factor */ | ||
37 | .byte 8 /* Return address register column */ | ||
38 | .uleb128 1 /* Augmentation value length */ | ||
39 | .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */ | ||
40 | .byte 0x0c /* DW_CFA_def_cfa */ | ||
41 | .uleb128 4 | ||
42 | .uleb128 4 | ||
43 | .byte 0x88 /* DW_CFA_offset, column 0x8 */ | ||
44 | .uleb128 1 | ||
45 | .align 4 | ||
46 | .LENDCIE: | ||
47 | |||
48 | .long .LENDFDE1-.LSTARTFDE1 /* Length FDE */ | ||
49 | .LSTARTFDE1: | ||
50 | .long .LSTARTFDE1-.LSTARTFRAME /* CIE pointer */ | ||
51 | .long .LSTART_vsyscall-. /* PC-relative start address */ | ||
52 | .long .LEND_vsyscall-.LSTART_vsyscall | ||
53 | .uleb128 0 /* Augmentation length */ | ||
54 | /* What follows are the instructions for the table generation. | ||
55 | We have to record all changes of the stack pointer. */ | ||
56 | .byte 0x40 + .Lpush_ebp-.LSTART_vsyscall /* DW_CFA_advance_loc */ | ||
57 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
58 | .uleb128 8 | ||
59 | .byte 0x85, 0x02 /* DW_CFA_offset %ebp -8 */ | ||
60 | .byte 0x40 + .Lpop_ebp-.Lpush_ebp /* DW_CFA_advance_loc */ | ||
61 | .byte 0xc5 /* DW_CFA_restore %ebp */ | ||
62 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
63 | .uleb128 4 | ||
64 | .align 4 | ||
65 | .LENDFDE1: | ||
66 | |||
67 | #define SYSCALL_ENTER_KERNEL syscall | ||
68 | #include "vsyscall-sigreturn.S" | ||
diff --git a/arch/x86_64/ia32/vsyscall-sysenter.S b/arch/x86_64/ia32/vsyscall-sysenter.S new file mode 100644 index 000000000000..8fb8e0ff3afa --- /dev/null +++ b/arch/x86_64/ia32/vsyscall-sysenter.S | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * Code for the vsyscall page. This version uses the sysenter instruction. | ||
3 | */ | ||
4 | |||
5 | #include <asm/ia32_unistd.h> | ||
6 | #include <asm/offset.h> | ||
7 | |||
8 | .text | ||
9 | .section .text.vsyscall,"ax" | ||
10 | .globl __kernel_vsyscall | ||
11 | .type __kernel_vsyscall,@function | ||
12 | __kernel_vsyscall: | ||
13 | .LSTART_vsyscall: | ||
14 | push %ecx | ||
15 | .Lpush_ecx: | ||
16 | push %edx | ||
17 | .Lpush_edx: | ||
18 | push %ebp | ||
19 | .Lenter_kernel: | ||
20 | movl %esp,%ebp | ||
21 | sysenter | ||
22 | .space 7,0x90 | ||
23 | jmp .Lenter_kernel | ||
24 | /* 16: System call normal return point is here! */ | ||
25 | pop %ebp | ||
26 | .Lpop_ebp: | ||
27 | pop %edx | ||
28 | .Lpop_edx: | ||
29 | pop %ecx | ||
30 | .Lpop_ecx: | ||
31 | ret | ||
32 | .LEND_vsyscall: | ||
33 | .size __kernel_vsyscall,.-.LSTART_vsyscall | ||
34 | |||
35 | .section .eh_frame,"a",@progbits | ||
36 | .LSTARTFRAME: | ||
37 | .long .LENDCIE-.LSTARTCIE | ||
38 | .LSTARTCIE: | ||
39 | .long 0 /* CIE ID */ | ||
40 | .byte 1 /* Version number */ | ||
41 | .string "zR" /* NUL-terminated augmentation string */ | ||
42 | .uleb128 1 /* Code alignment factor */ | ||
43 | .sleb128 -4 /* Data alignment factor */ | ||
44 | .byte 8 /* Return address register column */ | ||
45 | .uleb128 1 /* Augmentation value length */ | ||
46 | .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */ | ||
47 | .byte 0x0c /* DW_CFA_def_cfa */ | ||
48 | .uleb128 4 | ||
49 | .uleb128 4 | ||
50 | .byte 0x88 /* DW_CFA_offset, column 0x8 */ | ||
51 | .uleb128 1 | ||
52 | .align 4 | ||
53 | .LENDCIE: | ||
54 | |||
55 | .long .LENDFDE1-.LSTARTFDE1 /* Length FDE */ | ||
56 | .LSTARTFDE1: | ||
57 | .long .LSTARTFDE1-.LSTARTFRAME /* CIE pointer */ | ||
58 | .long .LSTART_vsyscall-. /* PC-relative start address */ | ||
59 | .long .LEND_vsyscall-.LSTART_vsyscall | ||
60 | .uleb128 0 /* Augmentation length */ | ||
61 | /* What follows are the instructions for the table generation. | ||
62 | We have to record all changes of the stack pointer. */ | ||
63 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
64 | .long .Lpush_ecx-.LSTART_vsyscall | ||
65 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
66 | .byte 0x08 /* RA at offset 8 now */ | ||
67 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
68 | .long .Lpush_edx-.Lpush_ecx | ||
69 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
70 | .byte 0x0c /* RA at offset 12 now */ | ||
71 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
72 | .long .Lenter_kernel-.Lpush_edx | ||
73 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
74 | .byte 0x10 /* RA at offset 16 now */ | ||
75 | .byte 0x85, 0x04 /* DW_CFA_offset %ebp -16 */ | ||
76 | /* Finally the epilogue. */ | ||
77 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
78 | .long .Lpop_ebp-.Lenter_kernel | ||
79 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
80 | .byte 0x12 /* RA at offset 12 now */ | ||
81 | .byte 0xc5 /* DW_CFA_restore %ebp */ | ||
82 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
83 | .long .Lpop_edx-.Lpop_ebp | ||
84 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
85 | .byte 0x08 /* RA at offset 8 now */ | ||
86 | .byte 0x04 /* DW_CFA_advance_loc4 */ | ||
87 | .long .Lpop_ecx-.Lpop_edx | ||
88 | .byte 0x0e /* DW_CFA_def_cfa_offset */ | ||
89 | .byte 0x04 /* RA at offset 4 now */ | ||
90 | .align 4 | ||
91 | .LENDFDE1: | ||
92 | |||
93 | #define SYSCALL_ENTER_KERNEL int $0x80 | ||
94 | #include "vsyscall-sigreturn.S" | ||
diff --git a/arch/x86_64/ia32/vsyscall.lds b/arch/x86_64/ia32/vsyscall.lds new file mode 100644 index 000000000000..fa4b4dd4a9ff --- /dev/null +++ b/arch/x86_64/ia32/vsyscall.lds | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * Linker script for vsyscall DSO. The vsyscall page is an ELF shared | ||
3 | * object prelinked to its virtual address. This script controls its layout. | ||
4 | */ | ||
5 | |||
6 | /* This must match <asm/fixmap.h>. */ | ||
7 | VSYSCALL_BASE = 0xffffe000; | ||
8 | |||
9 | SECTIONS | ||
10 | { | ||
11 | . = VSYSCALL_BASE + SIZEOF_HEADERS; | ||
12 | |||
13 | .hash : { *(.hash) } :text | ||
14 | .dynsym : { *(.dynsym) } | ||
15 | .dynstr : { *(.dynstr) } | ||
16 | .gnu.version : { *(.gnu.version) } | ||
17 | .gnu.version_d : { *(.gnu.version_d) } | ||
18 | .gnu.version_r : { *(.gnu.version_r) } | ||
19 | |||
20 | /* This linker script is used both with -r and with -shared. | ||
21 | For the layouts to match, we need to skip more than enough | ||
22 | space for the dynamic symbol table et al. If this amount | ||
23 | is insufficient, ld -shared will barf. Just increase it here. */ | ||
24 | . = VSYSCALL_BASE + 0x400; | ||
25 | |||
26 | .text.vsyscall : { *(.text.vsyscall) } :text =0x90909090 | ||
27 | |||
28 | /* This is an 32bit object and we cannot easily get the offsets | ||
29 | into the 64bit kernel. Just hardcode them here. This assumes | ||
30 | that all the stubs don't need more than 0x100 bytes. */ | ||
31 | . = VSYSCALL_BASE + 0x500; | ||
32 | |||
33 | .text.sigreturn : { *(.text.sigreturn) } :text =0x90909090 | ||
34 | |||
35 | . = VSYSCALL_BASE + 0x600; | ||
36 | |||
37 | .text.rtsigreturn : { *(.text.rtsigreturn) } :text =0x90909090 | ||
38 | |||
39 | .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr | ||
40 | .eh_frame : { KEEP (*(.eh_frame)) } :text | ||
41 | .dynamic : { *(.dynamic) } :text :dynamic | ||
42 | .useless : { | ||
43 | *(.got.plt) *(.got) | ||
44 | *(.data .data.* .gnu.linkonce.d.*) | ||
45 | *(.dynbss) | ||
46 | *(.bss .bss.* .gnu.linkonce.b.*) | ||
47 | } :text | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * We must supply the ELF program headers explicitly to get just one | ||
52 | * PT_LOAD segment, and set the flags explicitly to make segments read-only. | ||
53 | */ | ||
54 | PHDRS | ||
55 | { | ||
56 | text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ | ||
57 | dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ | ||
58 | eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */ | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * This controls what symbols we export from the DSO. | ||
63 | */ | ||
64 | VERSION | ||
65 | { | ||
66 | LINUX_2.5 { | ||
67 | global: | ||
68 | __kernel_vsyscall; | ||
69 | __kernel_sigreturn; | ||
70 | __kernel_rt_sigreturn; | ||
71 | |||
72 | local: *; | ||
73 | }; | ||
74 | } | ||
75 | |||
76 | /* The ELF entry point can be used to set the AT_SYSINFO value. */ | ||
77 | ENTRY(__kernel_vsyscall); | ||