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