Merge branch 'upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/linville...
[linux-2.6] / arch / i386 / kernel / sys_i386.c
1 /*
2  * linux/arch/i386/kernel/sys_i386.c
3  *
4  * This file contains various random system calls that
5  * have a non-standard calling sequence on the Linux/i386
6  * platform.
7  */
8
9 #include <linux/errno.h>
10 #include <linux/sched.h>
11 #include <linux/mm.h>
12 #include <linux/smp.h>
13 #include <linux/smp_lock.h>
14 #include <linux/sem.h>
15 #include <linux/msg.h>
16 #include <linux/shm.h>
17 #include <linux/stat.h>
18 #include <linux/syscalls.h>
19 #include <linux/mman.h>
20 #include <linux/file.h>
21 #include <linux/utsname.h>
22
23 #include <asm/uaccess.h>
24 #include <asm/unistd.h>
25 #include <asm/ipc.h>
26
27 /*
28  * sys_pipe() is the normal C calling standard for creating
29  * a pipe. It's not the way Unix traditionally does this, though.
30  */
31 asmlinkage int sys_pipe(unsigned long __user * fildes)
32 {
33         int fd[2];
34         int error;
35
36         error = do_pipe(fd);
37         if (!error) {
38                 if (copy_to_user(fildes, fd, 2*sizeof(int)))
39                         error = -EFAULT;
40         }
41         return error;
42 }
43
44 asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
45                           unsigned long prot, unsigned long flags,
46                           unsigned long fd, unsigned long pgoff)
47 {
48         int error = -EBADF;
49         struct file *file = NULL;
50         struct mm_struct *mm = current->mm;
51
52         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
53         if (!(flags & MAP_ANONYMOUS)) {
54                 file = fget(fd);
55                 if (!file)
56                         goto out;
57         }
58
59         down_write(&mm->mmap_sem);
60         error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
61         up_write(&mm->mmap_sem);
62
63         if (file)
64                 fput(file);
65 out:
66         return error;
67 }
68
69 /*
70  * Perform the select(nd, in, out, ex, tv) and mmap() system
71  * calls. Linux/i386 didn't use to be able to handle more than
72  * 4 system call parameters, so these system calls used a memory
73  * block for parameter passing..
74  */
75
76 struct mmap_arg_struct {
77         unsigned long addr;
78         unsigned long len;
79         unsigned long prot;
80         unsigned long flags;
81         unsigned long fd;
82         unsigned long offset;
83 };
84
85 asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
86 {
87         struct mmap_arg_struct a;
88         int err = -EFAULT;
89
90         if (copy_from_user(&a, arg, sizeof(a)))
91                 goto out;
92
93         err = -EINVAL;
94         if (a.offset & ~PAGE_MASK)
95                 goto out;
96
97         err = sys_mmap2(a.addr, a.len, a.prot, a.flags,
98                         a.fd, a.offset >> PAGE_SHIFT);
99 out:
100         return err;
101 }
102
103
104 struct sel_arg_struct {
105         unsigned long n;
106         fd_set __user *inp, *outp, *exp;
107         struct timeval __user *tvp;
108 };
109
110 asmlinkage int old_select(struct sel_arg_struct __user *arg)
111 {
112         struct sel_arg_struct a;
113
114         if (copy_from_user(&a, arg, sizeof(a)))
115                 return -EFAULT;
116         /* sys_select() does the appropriate kernel locking */
117         return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
118 }
119
120 /*
121  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
122  *
123  * This is really horribly ugly.
124  */
125 asmlinkage int sys_ipc (uint call, int first, int second,
126                         int third, void __user *ptr, long fifth)
127 {
128         int version, ret;
129
130         version = call >> 16; /* hack for backward compatibility */
131         call &= 0xffff;
132
133         switch (call) {
134         case SEMOP:
135                 return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
136         case SEMTIMEDOP:
137                 return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
138                                         (const struct timespec __user *)fifth);
139
140         case SEMGET:
141                 return sys_semget (first, second, third);
142         case SEMCTL: {
143                 union semun fourth;
144                 if (!ptr)
145                         return -EINVAL;
146                 if (get_user(fourth.__pad, (void __user * __user *) ptr))
147                         return -EFAULT;
148                 return sys_semctl (first, second, third, fourth);
149         }
150
151         case MSGSND:
152                 return sys_msgsnd (first, (struct msgbuf __user *) ptr, 
153                                    second, third);
154         case MSGRCV:
155                 switch (version) {
156                 case 0: {
157                         struct ipc_kludge tmp;
158                         if (!ptr)
159                                 return -EINVAL;
160                         
161                         if (copy_from_user(&tmp,
162                                            (struct ipc_kludge __user *) ptr, 
163                                            sizeof (tmp)))
164                                 return -EFAULT;
165                         return sys_msgrcv (first, tmp.msgp, second,
166                                            tmp.msgtyp, third);
167                 }
168                 default:
169                         return sys_msgrcv (first,
170                                            (struct msgbuf __user *) ptr,
171                                            second, fifth, third);
172                 }
173         case MSGGET:
174                 return sys_msgget ((key_t) first, second);
175         case MSGCTL:
176                 return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
177
178         case SHMAT:
179                 switch (version) {
180                 default: {
181                         ulong raddr;
182                         ret = do_shmat (first, (char __user *) ptr, second, &raddr);
183                         if (ret)
184                                 return ret;
185                         return put_user (raddr, (ulong __user *) third);
186                 }
187                 case 1: /* iBCS2 emulator entry point */
188                         if (!segment_eq(get_fs(), get_ds()))
189                                 return -EINVAL;
190                         /* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
191                         return do_shmat (first, (char __user *) ptr, second, (ulong *) third);
192                 }
193         case SHMDT: 
194                 return sys_shmdt ((char __user *)ptr);
195         case SHMGET:
196                 return sys_shmget (first, second, third);
197         case SHMCTL:
198                 return sys_shmctl (first, second,
199                                    (struct shmid_ds __user *) ptr);
200         default:
201                 return -ENOSYS;
202         }
203 }
204
205 /*
206  * Old cruft
207  */
208 asmlinkage int sys_uname(struct old_utsname __user * name)
209 {
210         int err;
211         if (!name)
212                 return -EFAULT;
213         down_read(&uts_sem);
214         err = copy_to_user(name, utsname(), sizeof (*name));
215         up_read(&uts_sem);
216         return err?-EFAULT:0;
217 }
218
219 asmlinkage int sys_olduname(struct oldold_utsname __user * name)
220 {
221         int error;
222
223         if (!name)
224                 return -EFAULT;
225         if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
226                 return -EFAULT;
227   
228         down_read(&uts_sem);
229         
230         error = __copy_to_user(&name->sysname, &utsname()->sysname,
231                                __OLD_UTS_LEN);
232         error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
233         error |= __copy_to_user(&name->nodename, &utsname()->nodename,
234                                 __OLD_UTS_LEN);
235         error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
236         error |= __copy_to_user(&name->release, &utsname()->release,
237                                 __OLD_UTS_LEN);
238         error |= __put_user(0, name->release + __OLD_UTS_LEN);
239         error |= __copy_to_user(&name->version, &utsname()->version,
240                                 __OLD_UTS_LEN);
241         error |= __put_user(0, name->version + __OLD_UTS_LEN);
242         error |= __copy_to_user(&name->machine, &utsname()->machine,
243                                 __OLD_UTS_LEN);
244         error |= __put_user(0, name->machine + __OLD_UTS_LEN);
245         
246         up_read(&uts_sem);
247         
248         error = error ? -EFAULT : 0;
249
250         return error;
251 }
252
253
254 /*
255  * Do a system call from kernel instead of calling sys_execve so we
256  * end up with proper pt_regs.
257  */
258 int kernel_execve(const char *filename, char *const argv[], char *const envp[])
259 {
260         long __res;
261         asm volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx"
262         : "=a" (__res)
263         : "0" (__NR_execve),"ri" (filename),"c" (argv), "d" (envp) : "memory");
264         return __res;
265 }