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