a8cbb202b8cd482f8deb05e68287341600c44637
[linux-2.6.git] / arch / ppc64 / kernel / syscalls.c
1 /*
2  * linux/arch/ppc64/kernel/sys_ppc.c
3  *
4  *  PowerPC version 
5  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
6  *
7  * Derived from "arch/i386/kernel/sys_i386.c"
8  * Adapted from the i386 version by Gary Thomas
9  * Modified by Cort Dougan (cort@cs.nmt.edu)
10  * and Paul Mackerras (paulus@cs.anu.edu.au).
11  *
12  * This file contains various random system calls that
13  * have a non-standard calling sequence on the Linux/PPC
14  * platform.
15  *
16  *  This program is free software; you can redistribute it and/or
17  *  modify it under the terms of the GNU General Public License
18  *  as published by the Free Software Foundation; either version
19  *  2 of the License, or (at your option) any later version.
20  *
21  */
22
23 #include <linux/errno.h>
24 #include <linux/sched.h>
25 #include <linux/syscalls.h>
26 #include <linux/mm.h>
27 #include <linux/smp.h>
28 #include <linux/smp_lock.h>
29 #include <linux/sem.h>
30 #include <linux/msg.h>
31 #include <linux/shm.h>
32 #include <linux/stat.h>
33 #include <linux/mman.h>
34 #include <linux/sys.h>
35 #include <linux/ipc.h>
36 #include <linux/utsname.h>
37 #include <linux/file.h>
38 #include <linux/init.h>
39 #include <linux/personality.h>
40
41 #include <asm/uaccess.h>
42 #include <asm/ipc.h>
43 #include <asm/semaphore.h>
44 #include <asm/time.h>
45 #include <asm/unistd.h>
46
47 extern unsigned long wall_jiffies;
48
49 void
50 check_bugs(void)
51 {
52 }
53
54 /*
55  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
56  *
57  * This is really horribly ugly.
58  */
59 asmlinkage int 
60 sys_ipc (uint call, int first, unsigned long second, long third,
61          void __user *ptr, long fifth)
62 {
63         int version, ret;
64
65         version = call >> 16; /* hack for backward compatibility */
66         call &= 0xffff;
67
68         ret = -ENOSYS;
69         switch (call) {
70         case SEMOP:
71                 ret = sys_semtimedop(first, (struct sembuf __user *)ptr,
72                                       (unsigned)second, NULL);
73                 break;
74         case SEMTIMEDOP:
75                 ret = sys_semtimedop(first, (struct sembuf __user *)ptr,
76                                       (unsigned)second,
77                                       (const struct timespec __user *) fifth);
78                 break;
79         case SEMGET:
80                 ret = sys_semget (first, (int)second, third);
81                 break;
82         case SEMCTL: {
83                 union semun fourth;
84
85                 ret = -EINVAL;
86                 if (!ptr)
87                         break;
88                 if ((ret = get_user(fourth.__pad, (void __user * __user *)ptr)))
89                         break;
90                 ret = sys_semctl(first, (int)second, third, fourth);
91                 break;
92         }
93         case MSGSND:
94                 ret = sys_msgsnd(first, (struct msgbuf __user *)ptr,
95                                   (size_t)second, third);
96                 break;
97         case MSGRCV:
98                 switch (version) {
99                 case 0: {
100                         struct ipc_kludge tmp;
101
102                         ret = -EINVAL;
103                         if (!ptr)
104                                 break;
105                         if ((ret = copy_from_user(&tmp,
106                                                 (struct ipc_kludge __user *) ptr,
107                                                 sizeof (tmp)) ? -EFAULT : 0))
108                                 break;
109                         ret = sys_msgrcv(first, tmp.msgp, (size_t) second,
110                                           tmp.msgtyp, third);
111                         break;
112                 }
113                 default:
114                         ret = sys_msgrcv (first, (struct msgbuf __user *) ptr,
115                                           (size_t)second, fifth, third);
116                         break;
117                 }
118                 break;
119         case MSGGET:
120                 ret = sys_msgget ((key_t)first, (int)second);
121                 break;
122         case MSGCTL:
123                 ret = sys_msgctl(first, (int)second,
124                                   (struct msqid_ds __user *)ptr);
125                 break;
126         case SHMAT:
127                 switch (version) {
128                 default: {
129                         ulong raddr;
130                         ret = do_shmat(first, (char __user *) ptr,
131                                         (int)second, &raddr);
132                         if (ret)
133                                 break;
134                         ret = put_user (raddr, (ulong __user *) third);
135                         break;
136                 }
137                 case 1: /* iBCS2 emulator entry point */
138                         ret = -EINVAL;
139                         if (!segment_eq(get_fs(), get_ds()))
140                                 break;
141                         ret = do_shmat(first, (char __user *)ptr,
142                                         (int)second, (ulong *)third);
143                         break;
144                 }
145                 break;
146         case SHMDT: 
147                 ret = sys_shmdt ((char __user *)ptr);
148                 break;
149         case SHMGET:
150                 ret = sys_shmget (first, (size_t)second, third);
151                 break;
152         case SHMCTL:
153                 ret = sys_shmctl(first, (int)second,
154                                   (struct shmid_ds __user *)ptr);
155                 break;
156         }
157
158         return ret;
159 }
160
161 /*
162  * sys_pipe() is the normal C calling standard for creating
163  * a pipe. It's not the way unix traditionally does this, though.
164  */
165 asmlinkage int sys_pipe(int __user *fildes)
166 {
167         int fd[2];
168         int error;
169         
170         error = do_pipe(fd);
171         if (!error) {
172                 if (copy_to_user(fildes, fd, 2*sizeof(int)))
173                         error = -EFAULT;
174         }
175         
176         return error;
177 }
178
179 unsigned long sys_mmap(unsigned long addr, size_t len,
180                        unsigned long prot, unsigned long flags,
181                        unsigned long fd, off_t offset)
182 {
183         struct file * file = NULL;
184         unsigned long ret = -EBADF;
185
186         if (!(flags & MAP_ANONYMOUS)) {
187                 if (!(file = fget(fd)))
188                         goto out;
189         }
190
191         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
192         down_write(&current->mm->mmap_sem);
193         ret = do_mmap(file, addr, len, prot, flags, offset);
194         up_write(&current->mm->mmap_sem);
195         if (file)
196                 fput(file);
197
198 out:
199         return ret;
200 }
201
202 long ppc64_personality(unsigned long personality)
203 {
204         long ret;
205
206         if (personality(current->personality) == PER_LINUX32
207             && personality == PER_LINUX)
208                 personality = PER_LINUX32;
209         ret = sys_personality(personality);
210         if (ret == PER_LINUX32)
211                 ret = PER_LINUX;
212         return ret;
213 }
214
215 long ppc64_newuname(struct new_utsname __user * name)
216 {
217         int err = 0;
218
219         down_read(&uts_sem);
220         if (copy_to_user(name, &system_utsname, sizeof(*name)))
221                 err = -EFAULT;
222         up_read(&uts_sem);
223         if (!err && personality(current->personality) == PER_LINUX32) {
224                 /* change ppc64 to ppc */
225                 if (__put_user(0, name->machine + 3)
226                     || __put_user(0, name->machine + 4))
227                         err = -EFAULT;
228         }
229         return err;
230 }
231
232 asmlinkage time_t sys64_time(time_t __user * tloc)
233 {
234         time_t secs;
235         time_t usecs;
236
237         long tb_delta = tb_ticks_since(tb_last_stamp);
238         tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy;
239
240         secs  = xtime.tv_sec;  
241         usecs = (xtime.tv_nsec/1000) + tb_delta / tb_ticks_per_usec;
242         while (usecs >= USEC_PER_SEC) {
243                 ++secs;
244                 usecs -= USEC_PER_SEC;
245         }
246
247         if (tloc) {
248                 if (put_user(secs,tloc))
249                         secs = -EFAULT;
250         }
251
252         return secs;
253 }
254
255 void do_show_syscall(unsigned long r3, unsigned long r4, unsigned long r5,
256                      unsigned long r6, unsigned long r7, unsigned long r8,
257                      struct pt_regs *regs)
258 {
259         printk("syscall %ld(%lx, %lx, %lx, %lx, %lx, %lx) regs=%p current=%p"
260                " cpu=%d\n", regs->gpr[0], r3, r4, r5, r6, r7, r8, regs,
261                current, smp_processor_id());
262 }
263
264 void do_show_syscall_exit(unsigned long r3)
265 {
266         printk(" -> %lx, current=%p cpu=%d\n", r3, current, smp_processor_id());
267 }