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