unified (weak) sys_pipe implementation
[safe/jmp/linux-2.6] / arch / m68k / kernel / sys_m68k.c
1 /*
2  * linux/arch/m68k/kernel/sys_m68k.c
3  *
4  * This file contains various random system calls that
5  * have a non-standard calling sequence on the Linux/m68k
6  * platform.
7  */
8
9 #include <linux/capability.h>
10 #include <linux/errno.h>
11 #include <linux/sched.h>
12 #include <linux/mm.h>
13 #include <linux/fs.h>
14 #include <linux/smp.h>
15 #include <linux/smp_lock.h>
16 #include <linux/sem.h>
17 #include <linux/msg.h>
18 #include <linux/shm.h>
19 #include <linux/stat.h>
20 #include <linux/syscalls.h>
21 #include <linux/mman.h>
22 #include <linux/file.h>
23 #include <linux/utsname.h>
24 #include <linux/ipc.h>
25
26 #include <asm/setup.h>
27 #include <asm/uaccess.h>
28 #include <asm/cachectl.h>
29 #include <asm/traps.h>
30 #include <asm/page.h>
31 #include <asm/unistd.h>
32
33 /* common code for old and new mmaps */
34 static inline long do_mmap2(
35         unsigned long addr, unsigned long len,
36         unsigned long prot, unsigned long flags,
37         unsigned long fd, unsigned long pgoff)
38 {
39         int error = -EBADF;
40         struct file * file = NULL;
41
42         flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
43         if (!(flags & MAP_ANONYMOUS)) {
44                 file = fget(fd);
45                 if (!file)
46                         goto out;
47         }
48
49         down_write(&current->mm->mmap_sem);
50         error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
51         up_write(&current->mm->mmap_sem);
52
53         if (file)
54                 fput(file);
55 out:
56         return error;
57 }
58
59 asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
60         unsigned long prot, unsigned long flags,
61         unsigned long fd, unsigned long pgoff)
62 {
63         return do_mmap2(addr, len, prot, flags, fd, pgoff);
64 }
65
66 /*
67  * Perform the select(nd, in, out, ex, tv) and mmap() system
68  * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
69  * handle more than 4 system call parameters, so these system calls
70  * used a memory block for parameter passing..
71  */
72
73 struct mmap_arg_struct {
74         unsigned long addr;
75         unsigned long len;
76         unsigned long prot;
77         unsigned long flags;
78         unsigned long fd;
79         unsigned long offset;
80 };
81
82 asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
83 {
84         struct mmap_arg_struct a;
85         int error = -EFAULT;
86
87         if (copy_from_user(&a, arg, sizeof(a)))
88                 goto out;
89
90         error = -EINVAL;
91         if (a.offset & ~PAGE_MASK)
92                 goto out;
93
94         a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
95
96         error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
97 out:
98         return error;
99 }
100
101 #if 0
102 struct mmap_arg_struct64 {
103         __u32 addr;
104         __u32 len;
105         __u32 prot;
106         __u32 flags;
107         __u64 offset; /* 64 bits */
108         __u32 fd;
109 };
110
111 asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
112 {
113         int error = -EFAULT;
114         struct file * file = NULL;
115         struct mmap_arg_struct64 a;
116         unsigned long pgoff;
117
118         if (copy_from_user(&a, arg, sizeof(a)))
119                 return -EFAULT;
120
121         if ((long)a.offset & ~PAGE_MASK)
122                 return -EINVAL;
123
124         pgoff = a.offset >> PAGE_SHIFT;
125         if ((a.offset >> PAGE_SHIFT) != pgoff)
126                 return -EINVAL;
127
128         if (!(a.flags & MAP_ANONYMOUS)) {
129                 error = -EBADF;
130                 file = fget(a.fd);
131                 if (!file)
132                         goto out;
133         }
134         a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
135
136         down_write(&current->mm->mmap_sem);
137         error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
138         up_write(&current->mm->mmap_sem);
139         if (file)
140                 fput(file);
141 out:
142         return error;
143 }
144 #endif
145
146 struct sel_arg_struct {
147         unsigned long n;
148         fd_set __user *inp, *outp, *exp;
149         struct timeval __user *tvp;
150 };
151
152 asmlinkage int old_select(struct sel_arg_struct __user *arg)
153 {
154         struct sel_arg_struct a;
155
156         if (copy_from_user(&a, arg, sizeof(a)))
157                 return -EFAULT;
158         /* sys_select() does the appropriate kernel locking */
159         return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
160 }
161
162 /*
163  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
164  *
165  * This is really horribly ugly.
166  */
167 asmlinkage int sys_ipc (uint call, int first, int second,
168                         int third, void __user *ptr, long fifth)
169 {
170         int version, ret;
171
172         version = call >> 16; /* hack for backward compatibility */
173         call &= 0xffff;
174
175         if (call <= SEMCTL)
176                 switch (call) {
177                 case SEMOP:
178                         return sys_semop (first, ptr, second);
179                 case SEMGET:
180                         return sys_semget (first, second, third);
181                 case SEMCTL: {
182                         union semun fourth;
183                         if (!ptr)
184                                 return -EINVAL;
185                         if (get_user(fourth.__pad, (void __user *__user *) ptr))
186                                 return -EFAULT;
187                         return sys_semctl (first, second, third, fourth);
188                         }
189                 default:
190                         return -ENOSYS;
191                 }
192         if (call <= MSGCTL)
193                 switch (call) {
194                 case MSGSND:
195                         return sys_msgsnd (first, ptr, second, third);
196                 case MSGRCV:
197                         switch (version) {
198                         case 0: {
199                                 struct ipc_kludge tmp;
200                                 if (!ptr)
201                                         return -EINVAL;
202                                 if (copy_from_user (&tmp, ptr, sizeof (tmp)))
203                                         return -EFAULT;
204                                 return sys_msgrcv (first, tmp.msgp, second,
205                                                    tmp.msgtyp, third);
206                                 }
207                         default:
208                                 return sys_msgrcv (first, ptr,
209                                                    second, fifth, third);
210                         }
211                 case MSGGET:
212                         return sys_msgget ((key_t) first, second);
213                 case MSGCTL:
214                         return sys_msgctl (first, second, ptr);
215                 default:
216                         return -ENOSYS;
217                 }
218         if (call <= SHMCTL)
219                 switch (call) {
220                 case SHMAT:
221                         switch (version) {
222                         default: {
223                                 ulong raddr;
224                                 ret = do_shmat (first, ptr, second, &raddr);
225                                 if (ret)
226                                         return ret;
227                                 return put_user (raddr, (ulong __user *) third);
228                         }
229                         }
230                 case SHMDT:
231                         return sys_shmdt (ptr);
232                 case SHMGET:
233                         return sys_shmget (first, second, third);
234                 case SHMCTL:
235                         return sys_shmctl (first, second, ptr);
236                 default:
237                         return -ENOSYS;
238                 }
239
240         return -EINVAL;
241 }
242
243 /* Convert virtual (user) address VADDR to physical address PADDR */
244 #define virt_to_phys_040(vaddr)                                         \
245 ({                                                                      \
246   unsigned long _mmusr, _paddr;                                         \
247                                                                         \
248   __asm__ __volatile__ (".chip 68040\n\t"                               \
249                         "ptestr (%1)\n\t"                               \
250                         "movec %%mmusr,%0\n\t"                          \
251                         ".chip 68k"                                     \
252                         : "=r" (_mmusr)                                 \
253                         : "a" (vaddr));                                 \
254   _paddr = (_mmusr & MMU_R_040) ? (_mmusr & PAGE_MASK) : 0;             \
255   _paddr;                                                               \
256 })
257
258 static inline int
259 cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len)
260 {
261   unsigned long paddr, i;
262
263   switch (scope)
264     {
265     case FLUSH_SCOPE_ALL:
266       switch (cache)
267         {
268         case FLUSH_CACHE_DATA:
269           /* This nop is needed for some broken versions of the 68040.  */
270           __asm__ __volatile__ ("nop\n\t"
271                                 ".chip 68040\n\t"
272                                 "cpusha %dc\n\t"
273                                 ".chip 68k");
274           break;
275         case FLUSH_CACHE_INSN:
276           __asm__ __volatile__ ("nop\n\t"
277                                 ".chip 68040\n\t"
278                                 "cpusha %ic\n\t"
279                                 ".chip 68k");
280           break;
281         default:
282         case FLUSH_CACHE_BOTH:
283           __asm__ __volatile__ ("nop\n\t"
284                                 ".chip 68040\n\t"
285                                 "cpusha %bc\n\t"
286                                 ".chip 68k");
287           break;
288         }
289       break;
290
291     case FLUSH_SCOPE_LINE:
292       /* Find the physical address of the first mapped page in the
293          address range.  */
294       if ((paddr = virt_to_phys_040(addr))) {
295         paddr += addr & ~(PAGE_MASK | 15);
296         len = (len + (addr & 15) + 15) >> 4;
297       } else {
298         unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK);
299
300         if (len <= tmp)
301           return 0;
302         addr += tmp;
303         len -= tmp;
304         tmp = PAGE_SIZE;
305         for (;;)
306           {
307             if ((paddr = virt_to_phys_040(addr)))
308               break;
309             if (len <= tmp)
310               return 0;
311             addr += tmp;
312             len -= tmp;
313           }
314         len = (len + 15) >> 4;
315       }
316       i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4;
317       while (len--)
318         {
319           switch (cache)
320             {
321             case FLUSH_CACHE_DATA:
322               __asm__ __volatile__ ("nop\n\t"
323                                     ".chip 68040\n\t"
324                                     "cpushl %%dc,(%0)\n\t"
325                                     ".chip 68k"
326                                     : : "a" (paddr));
327               break;
328             case FLUSH_CACHE_INSN:
329               __asm__ __volatile__ ("nop\n\t"
330                                     ".chip 68040\n\t"
331                                     "cpushl %%ic,(%0)\n\t"
332                                     ".chip 68k"
333                                     : : "a" (paddr));
334               break;
335             default:
336             case FLUSH_CACHE_BOTH:
337               __asm__ __volatile__ ("nop\n\t"
338                                     ".chip 68040\n\t"
339                                     "cpushl %%bc,(%0)\n\t"
340                                     ".chip 68k"
341                                     : : "a" (paddr));
342               break;
343             }
344           if (!--i && len)
345             {
346               /*
347                * No need to page align here since it is done by
348                * virt_to_phys_040().
349                */
350               addr += PAGE_SIZE;
351               i = PAGE_SIZE / 16;
352               /* Recompute physical address when crossing a page
353                  boundary. */
354               for (;;)
355                 {
356                   if ((paddr = virt_to_phys_040(addr)))
357                     break;
358                   if (len <= i)
359                     return 0;
360                   len -= i;
361                   addr += PAGE_SIZE;
362                 }
363             }
364           else
365             paddr += 16;
366         }
367       break;
368
369     default:
370     case FLUSH_SCOPE_PAGE:
371       len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1);
372       for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
373         {
374           if (!(paddr = virt_to_phys_040(addr)))
375             continue;
376           switch (cache)
377             {
378             case FLUSH_CACHE_DATA:
379               __asm__ __volatile__ ("nop\n\t"
380                                     ".chip 68040\n\t"
381                                     "cpushp %%dc,(%0)\n\t"
382                                     ".chip 68k"
383                                     : : "a" (paddr));
384               break;
385             case FLUSH_CACHE_INSN:
386               __asm__ __volatile__ ("nop\n\t"
387                                     ".chip 68040\n\t"
388                                     "cpushp %%ic,(%0)\n\t"
389                                     ".chip 68k"
390                                     : : "a" (paddr));
391               break;
392             default:
393             case FLUSH_CACHE_BOTH:
394               __asm__ __volatile__ ("nop\n\t"
395                                     ".chip 68040\n\t"
396                                     "cpushp %%bc,(%0)\n\t"
397                                     ".chip 68k"
398                                     : : "a" (paddr));
399               break;
400             }
401         }
402       break;
403     }
404   return 0;
405 }
406
407 #define virt_to_phys_060(vaddr)                         \
408 ({                                                      \
409   unsigned long paddr;                                  \
410   __asm__ __volatile__ (".chip 68060\n\t"               \
411                         "plpar (%0)\n\t"                \
412                         ".chip 68k"                     \
413                         : "=a" (paddr)                  \
414                         : "0" (vaddr));                 \
415   (paddr); /* XXX */                                    \
416 })
417
418 static inline int
419 cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len)
420 {
421   unsigned long paddr, i;
422
423   /*
424    * 68060 manual says:
425    *  cpush %dc : flush DC, remains valid (with our %cacr setup)
426    *  cpush %ic : invalidate IC
427    *  cpush %bc : flush DC + invalidate IC
428    */
429   switch (scope)
430     {
431     case FLUSH_SCOPE_ALL:
432       switch (cache)
433         {
434         case FLUSH_CACHE_DATA:
435           __asm__ __volatile__ (".chip 68060\n\t"
436                                 "cpusha %dc\n\t"
437                                 ".chip 68k");
438           break;
439         case FLUSH_CACHE_INSN:
440           __asm__ __volatile__ (".chip 68060\n\t"
441                                 "cpusha %ic\n\t"
442                                 ".chip 68k");
443           break;
444         default:
445         case FLUSH_CACHE_BOTH:
446           __asm__ __volatile__ (".chip 68060\n\t"
447                                 "cpusha %bc\n\t"
448                                 ".chip 68k");
449           break;
450         }
451       break;
452
453     case FLUSH_SCOPE_LINE:
454       /* Find the physical address of the first mapped page in the
455          address range.  */
456       len += addr & 15;
457       addr &= -16;
458       if (!(paddr = virt_to_phys_060(addr))) {
459         unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK);
460
461         if (len <= tmp)
462           return 0;
463         addr += tmp;
464         len -= tmp;
465         tmp = PAGE_SIZE;
466         for (;;)
467           {
468             if ((paddr = virt_to_phys_060(addr)))
469               break;
470             if (len <= tmp)
471               return 0;
472             addr += tmp;
473             len -= tmp;
474           }
475       }
476       len = (len + 15) >> 4;
477       i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4;
478       while (len--)
479         {
480           switch (cache)
481             {
482             case FLUSH_CACHE_DATA:
483               __asm__ __volatile__ (".chip 68060\n\t"
484                                     "cpushl %%dc,(%0)\n\t"
485                                     ".chip 68k"
486                                     : : "a" (paddr));
487               break;
488             case FLUSH_CACHE_INSN:
489               __asm__ __volatile__ (".chip 68060\n\t"
490                                     "cpushl %%ic,(%0)\n\t"
491                                     ".chip 68k"
492                                     : : "a" (paddr));
493               break;
494             default:
495             case FLUSH_CACHE_BOTH:
496               __asm__ __volatile__ (".chip 68060\n\t"
497                                     "cpushl %%bc,(%0)\n\t"
498                                     ".chip 68k"
499                                     : : "a" (paddr));
500               break;
501             }
502           if (!--i && len)
503             {
504
505               /*
506                * We just want to jump to the first cache line
507                * in the next page.
508                */
509               addr += PAGE_SIZE;
510               addr &= PAGE_MASK;
511
512               i = PAGE_SIZE / 16;
513               /* Recompute physical address when crossing a page
514                  boundary. */
515               for (;;)
516                 {
517                   if ((paddr = virt_to_phys_060(addr)))
518                     break;
519                   if (len <= i)
520                     return 0;
521                   len -= i;
522                   addr += PAGE_SIZE;
523                 }
524             }
525           else
526             paddr += 16;
527         }
528       break;
529
530     default:
531     case FLUSH_SCOPE_PAGE:
532       len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1);
533       addr &= PAGE_MASK;        /* Workaround for bug in some
534                                    revisions of the 68060 */
535       for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE)
536         {
537           if (!(paddr = virt_to_phys_060(addr)))
538             continue;
539           switch (cache)
540             {
541             case FLUSH_CACHE_DATA:
542               __asm__ __volatile__ (".chip 68060\n\t"
543                                     "cpushp %%dc,(%0)\n\t"
544                                     ".chip 68k"
545                                     : : "a" (paddr));
546               break;
547             case FLUSH_CACHE_INSN:
548               __asm__ __volatile__ (".chip 68060\n\t"
549                                     "cpushp %%ic,(%0)\n\t"
550                                     ".chip 68k"
551                                     : : "a" (paddr));
552               break;
553             default:
554             case FLUSH_CACHE_BOTH:
555               __asm__ __volatile__ (".chip 68060\n\t"
556                                     "cpushp %%bc,(%0)\n\t"
557                                     ".chip 68k"
558                                     : : "a" (paddr));
559               break;
560             }
561         }
562       break;
563     }
564   return 0;
565 }
566
567 /* sys_cacheflush -- flush (part of) the processor cache.  */
568 asmlinkage int
569 sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len)
570 {
571         struct vm_area_struct *vma;
572         int ret = -EINVAL;
573
574         lock_kernel();
575         if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL ||
576             cache & ~FLUSH_CACHE_BOTH)
577                 goto out;
578
579         if (scope == FLUSH_SCOPE_ALL) {
580                 /* Only the superuser may explicitly flush the whole cache. */
581                 ret = -EPERM;
582                 if (!capable(CAP_SYS_ADMIN))
583                         goto out;
584         } else {
585                 /*
586                  * Verify that the specified address region actually belongs
587                  * to this process.
588                  */
589                 vma = find_vma (current->mm, addr);
590                 ret = -EINVAL;
591                 /* Check for overflow.  */
592                 if (addr + len < addr)
593                         goto out;
594                 if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
595                         goto out;
596         }
597
598         if (CPU_IS_020_OR_030) {
599                 if (scope == FLUSH_SCOPE_LINE && len < 256) {
600                         unsigned long cacr;
601                         __asm__ ("movec %%cacr, %0" : "=r" (cacr));
602                         if (cache & FLUSH_CACHE_INSN)
603                                 cacr |= 4;
604                         if (cache & FLUSH_CACHE_DATA)
605                                 cacr |= 0x400;
606                         len >>= 2;
607                         while (len--) {
608                                 __asm__ __volatile__ ("movec %1, %%caar\n\t"
609                                                       "movec %0, %%cacr"
610                                                       : /* no outputs */
611                                                       : "r" (cacr), "r" (addr));
612                                 addr += 4;
613                         }
614                 } else {
615                         /* Flush the whole cache, even if page granularity requested. */
616                         unsigned long cacr;
617                         __asm__ ("movec %%cacr, %0" : "=r" (cacr));
618                         if (cache & FLUSH_CACHE_INSN)
619                                 cacr |= 8;
620                         if (cache & FLUSH_CACHE_DATA)
621                                 cacr |= 0x800;
622                         __asm__ __volatile__ ("movec %0, %%cacr" : : "r" (cacr));
623                 }
624                 ret = 0;
625                 goto out;
626         } else {
627             /*
628              * 040 or 060: don't blindly trust 'scope', someone could
629              * try to flush a few megs of memory.
630              */
631
632             if (len>=3*PAGE_SIZE && scope<FLUSH_SCOPE_PAGE)
633                 scope=FLUSH_SCOPE_PAGE;
634             if (len>=10*PAGE_SIZE && scope<FLUSH_SCOPE_ALL)
635                 scope=FLUSH_SCOPE_ALL;
636             if (CPU_IS_040) {
637                 ret = cache_flush_040 (addr, scope, cache, len);
638             } else if (CPU_IS_060) {
639                 ret = cache_flush_060 (addr, scope, cache, len);
640             }
641         }
642 out:
643         unlock_kernel();
644         return ret;
645 }
646
647 asmlinkage int sys_getpagesize(void)
648 {
649         return PAGE_SIZE;
650 }
651
652 /*
653  * Do a system call from kernel instead of calling sys_execve so we
654  * end up with proper pt_regs.
655  */
656 int kernel_execve(const char *filename, char *const argv[], char *const envp[])
657 {
658         register long __res asm ("%d0") = __NR_execve;
659         register long __a asm ("%d1") = (long)(filename);
660         register long __b asm ("%d2") = (long)(argv);
661         register long __c asm ("%d3") = (long)(envp);
662         asm volatile ("trap  #0" : "+d" (__res)
663                         : "d" (__a), "d" (__b), "d" (__c));
664         return __res;
665 }