File: /usr/src/linux/arch/sparc64/kernel/sys_sparc.c

1     /* $Id: sys_sparc.c,v 1.52 2001/04/14 01:12:02 davem Exp $
2      * linux/arch/sparc64/kernel/sys_sparc.c
3      *
4      * This file contains various random system calls that
5      * have a non-standard calling sequence on the Linux/sparc
6      * platform.
7      */
8     
9     #include <linux/config.h>
10     #include <linux/errno.h>
11     #include <linux/types.h>
12     #include <linux/sched.h>
13     #include <linux/fs.h>
14     #include <linux/file.h>
15     #include <linux/mm.h>
16     #include <linux/sem.h>
17     #include <linux/msg.h>
18     #include <linux/shm.h>
19     #include <linux/stat.h>
20     #include <linux/mman.h>
21     #include <linux/utsname.h>
22     #include <linux/smp.h>
23     #include <linux/smp_lock.h>
24     #include <linux/slab.h>
25     #include <linux/ipc.h>
26     #include <linux/personality.h>
27     
28     #include <asm/uaccess.h>
29     #include <asm/ipc.h>
30     #include <asm/utrap.h>
31     #include <asm/perfctr.h>
32     
33     /* #define DEBUG_UNIMP_SYSCALL */
34     
35     /* XXX Make this per-binary type, this way we can detect the type of
36      * XXX a binary.  Every Sparc executable calls this very early on.
37      */
38     asmlinkage unsigned long sys_getpagesize(void)
39     {
40     	return PAGE_SIZE;
41     }
42     
43     #define COLOUR_ALIGN(addr)	(((addr)+SHMLBA-1)&~(SHMLBA-1))
44     
45     unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
46     {
47     	struct vm_area_struct * vmm;
48     	unsigned long task_size = TASK_SIZE;
49     
50     	if (flags & MAP_FIXED) {
51     		/* We do not accept a shared mapping if it would violate
52     		 * cache aliasing constraints.
53     		 */
54     		if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
55     			return -EINVAL;
56     		return addr;
57     	}
58     
59     	if (current->thread.flags & SPARC_FLAG_32BIT)
60     		task_size = 0xf0000000UL;
61     	if (len > task_size || len > -PAGE_OFFSET)
62     		return -ENOMEM;
63     	if (!addr)
64     		addr = TASK_UNMAPPED_BASE;
65     
66     	if (flags & MAP_SHARED)
67     		addr = COLOUR_ALIGN(addr);
68     	else
69     		addr = PAGE_ALIGN(addr);
70     
71     	task_size -= len;
72     
73     	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
74     		/* At this point:  (!vmm || addr < vmm->vm_end). */
75     		if (addr < PAGE_OFFSET && -PAGE_OFFSET - len < addr) {
76     			addr = PAGE_OFFSET;
77     			vmm = find_vma(current->mm, PAGE_OFFSET);
78     		}
79     		if (task_size < addr)
80     			return -ENOMEM;
81     		if (!vmm || addr + len <= vmm->vm_start)
82     			return addr;
83     		addr = vmm->vm_end;
84     		if (flags & MAP_SHARED)
85     			addr = COLOUR_ALIGN(addr);
86     	}
87     }
88     
89     /* Try to align mapping such that we align it as much as possible. */
90     unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
91     {
92     	unsigned long align_goal, addr = -ENOMEM;
93     
94     	if (flags & MAP_FIXED) {
95     		/* Ok, don't mess with it. */
96     		return get_unmapped_area(NULL, addr, len, pgoff, flags);
97     	}
98     	flags &= ~MAP_SHARED;
99     
100     	align_goal = PAGE_SIZE;
101     	if (len >= (4UL * 1024 * 1024))
102     		align_goal = (4UL * 1024 * 1024);
103     	else if (len >= (512UL * 1024))
104     		align_goal = (512UL * 1024);
105     	else if (len >= (64UL * 1024))
106     		align_goal = (64UL * 1024);
107     
108     	do {
109     		addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
110     		if (!(addr & ~PAGE_MASK)) {
111     			addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
112     			break;
113     		}
114     
115     		if (align_goal == (4UL * 1024 * 1024))
116     			align_goal = (512UL * 1024);
117     		else if (align_goal == (512UL * 1024))
118     			align_goal = (64UL * 1024);
119     		else
120     			align_goal = PAGE_SIZE;
121     	} while ((addr & ~PAGE_MASK) && align_goal > PAGE_SIZE);
122     
123     	/* Mapping is smaller than 64K or larger areas could not
124     	 * be obtained.
125     	 */
126     	if (addr & ~PAGE_MASK)
127     		addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
128     
129     	return addr;
130     }
131     
132     extern asmlinkage unsigned long sys_brk(unsigned long brk);
133     
134     asmlinkage unsigned long sparc_brk(unsigned long brk)
135     {
136     	/* People could try to be nasty and use ta 0x6d in 32bit programs */
137     	if ((current->thread.flags & SPARC_FLAG_32BIT) &&
138     	    brk >= 0xf0000000UL)
139     		return current->mm->brk;
140     
141     	if ((current->mm->brk & PAGE_OFFSET) != (brk & PAGE_OFFSET))
142     		return current->mm->brk;
143     	return sys_brk(brk);
144     }
145                                                                     
146     /*
147      * sys_pipe() is the normal C calling standard for creating
148      * a pipe. It's not the way unix traditionally does this, though.
149      */
150     asmlinkage int sparc_pipe(struct pt_regs *regs)
151     {
152     	int fd[2];
153     	int error;
154     
155     	error = do_pipe(fd);
156     	if (error)
157     		goto out;
158     	regs->u_regs[UREG_I1] = fd[1];
159     	error = fd[0];
160     out:
161     	return error;
162     }
163     
164     /*
165      * sys_ipc() is the de-multiplexer for the SysV IPC calls..
166      *
167      * This is really horribly ugly.
168      */
169     
170     asmlinkage int sys_ipc (unsigned call, int first, int second, unsigned long third, void *ptr, long fifth)
171     {
172     	int err;
173     
174     	/* No need for backward compatibility. We can start fresh... */
175     
176     	if (call <= SEMCTL)
177     		switch (call) {
178     		case SEMOP:
179     			err = sys_semop (first, (struct sembuf *)ptr, second);
180     			goto out;
181     		case SEMGET:
182     			err = sys_semget (first, second, (int)third);
183     			goto out;
184     		case SEMCTL: {
185     			union semun fourth;
186     			err = -EINVAL;
187     			if (!ptr)
188     				goto out;
189     			err = -EFAULT;
190     			if(get_user(fourth.__pad, (void **)ptr))
191     				goto out;
192     			err = sys_semctl (first, second | IPC_64, (int)third, fourth);
193     			goto out;
194     			}
195     		default:
196     			err = -EINVAL;
197     			goto out;
198     		}
199     	if (call <= MSGCTL) 
200     		switch (call) {
201     		case MSGSND:
202     			err = sys_msgsnd (first, (struct msgbuf *) ptr, 
203     					  second, (int)third);
204     			goto out;
205     		case MSGRCV:
206     			err = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, (int)third);
207     			goto out;
208     		case MSGGET:
209     			err = sys_msgget ((key_t) first, second);
210     			goto out;
211     		case MSGCTL:
212     			err = sys_msgctl (first, second | IPC_64, (struct msqid_ds *) ptr);
213     			goto out;
214     		default:
215     			err = -EINVAL;
216     			goto out;
217     		}
218     	if (call <= SHMCTL) 
219     		switch (call) {
220     		case SHMAT:
221     			err = sys_shmat (first, (char *) ptr, second, (ulong *) third);
222     			goto out;
223     		case SHMDT:
224     			err = sys_shmdt ((char *)ptr);
225     			goto out;
226     		case SHMGET:
227     			err = sys_shmget (first, second, (int)third);
228     			goto out;
229     		case SHMCTL:
230     			err = sys_shmctl (first, second | IPC_64, (struct shmid_ds *) ptr);
231     			goto out;
232     		default:
233     			err = -EINVAL;
234     			goto out;
235     		}
236     	else
237     		err = -EINVAL;
238     out:
239     	return err;
240     }
241     
242     extern asmlinkage int sys_newuname(struct new_utsname * name);
243     
244     asmlinkage int sparc64_newuname(struct new_utsname * name)
245     {
246     	int ret = sys_newuname(name);
247     	
248     	if (current->personality == PER_LINUX32 && !ret) {
249     		ret = copy_to_user(name->machine, "sparc\0\0", 8);
250     	}
251     	return ret;
252     }
253     
254     extern asmlinkage long sys_personality(unsigned long);
255     
256     asmlinkage int sparc64_personality(unsigned long personality)
257     {
258     	int ret;
259     	if (current->personality == PER_LINUX32 && personality == PER_LINUX)
260     		personality = PER_LINUX32;
261     	ret = sys_personality(personality);
262     	if (ret == PER_LINUX32)
263     		ret = PER_LINUX;
264     	return ret;
265     }
266     
267     /* Linux version of mmap */
268     asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
269     	unsigned long prot, unsigned long flags, unsigned long fd,
270     	unsigned long off)
271     {
272     	struct file * file = NULL;
273     	unsigned long retval = -EBADF;
274     
275     	if (!(flags & MAP_ANONYMOUS)) {
276     		file = fget(fd);
277     		if (!file)
278     			goto out;
279     	}
280     	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
281     	len = PAGE_ALIGN(len);
282     	retval = -EINVAL;
283     
284     	if (current->thread.flags & SPARC_FLAG_32BIT) {
285     		if (len > 0xf0000000UL ||
286     		    ((flags & MAP_FIXED) && addr > 0xf0000000UL - len))
287     			goto out_putf;
288     	} else {
289     		if (len > -PAGE_OFFSET ||
290     		    ((flags & MAP_FIXED) &&
291     		     addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
292     			goto out_putf;
293     	}
294     
295     	down_write(&current->mm->mmap_sem);
296     	retval = do_mmap(file, addr, len, prot, flags, off);
297     	up_write(&current->mm->mmap_sem);
298     
299     out_putf:
300     	if (file)
301     		fput(file);
302     out:
303     	return retval;
304     }
305     
306     asmlinkage long sys64_munmap(unsigned long addr, size_t len)
307     {
308     	long ret;
309     
310     	if (len > -PAGE_OFFSET ||
311     	    (addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
312     		return -EINVAL;
313     	down_write(&current->mm->mmap_sem);
314     	ret = do_munmap(current->mm, addr, len);
315     	up_write(&current->mm->mmap_sem);
316     	return ret;
317     }
318     
319     extern unsigned long do_mremap(unsigned long addr,
320     	unsigned long old_len, unsigned long new_len,
321     	unsigned long flags, unsigned long new_addr);
322                     
323     asmlinkage unsigned long sys64_mremap(unsigned long addr,
324     	unsigned long old_len, unsigned long new_len,
325     	unsigned long flags, unsigned long new_addr)
326     {
327     	struct vm_area_struct *vma;
328     	unsigned long ret = -EINVAL;
329     	if (current->thread.flags & SPARC_FLAG_32BIT)
330     		goto out;
331     	if (old_len > -PAGE_OFFSET || new_len > -PAGE_OFFSET)
332     		goto out;
333     	if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET)
334     		goto out;
335     	down_write(&current->mm->mmap_sem);
336     	if (flags & MREMAP_FIXED) {
337     		if (new_addr < PAGE_OFFSET &&
338     		    new_addr + new_len > -PAGE_OFFSET)
339     			goto out_sem;
340     	} else if (addr < PAGE_OFFSET && addr + new_len > -PAGE_OFFSET) {
341     		unsigned long map_flags = 0;
342     		struct file *file = NULL;
343     
344     		ret = -ENOMEM;
345     		if (!(flags & MREMAP_MAYMOVE))
346     			goto out_sem;
347     
348     		vma = find_vma(current->mm, addr);
349     		if (vma) {
350     			if (vma->vm_flags & VM_SHARED)
351     				map_flags |= MAP_SHARED;
352     			file = vma->vm_file;
353     		}
354     
355     		/* MREMAP_FIXED checked above. */
356     		new_addr = get_unmapped_area(file, addr, new_len,
357     				    vma ? vma->vm_pgoff : 0,
358     				    map_flags);
359     		ret = new_addr;
360     		if (new_addr & ~PAGE_MASK)
361     			goto out_sem;
362     		flags |= MREMAP_FIXED;
363     	}
364     	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
365     out_sem:
366     	up_write(&current->mm->mmap_sem);
367     out:
368     	return ret;       
369     }
370     
371     /* we come to here via sys_nis_syscall so it can setup the regs argument */
372     asmlinkage unsigned long
373     c_sys_nis_syscall (struct pt_regs *regs)
374     {
375     	static int count=0;
376     	
377     	/* Don't make the system unusable, if someone goes stuck */
378     	if (count++ > 5)
379     		return -ENOSYS;
380     
381     	printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
382     #ifdef DEBUG_UNIMP_SYSCALL	
383     	show_regs (regs);
384     #endif
385     
386     	return -ENOSYS;
387     }
388     
389     /* #define DEBUG_SPARC_BREAKPOINT */
390     
391     asmlinkage void
392     sparc_breakpoint (struct pt_regs *regs)
393     {
394     	siginfo_t info;
395     
396     	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
397     		regs->tpc &= 0xffffffff;
398     		regs->tnpc &= 0xffffffff;
399     	}
400     #ifdef DEBUG_SPARC_BREAKPOINT
401             printk ("TRAP: Entering kernel PC=%lx, nPC=%lx\n", regs->tpc, regs->tnpc);
402     #endif
403     	info.si_signo = SIGTRAP;
404     	info.si_errno = 0;
405     	info.si_code = TRAP_BRKPT;
406     	info.si_addr = (void *)regs->tpc;
407     	info.si_trapno = 0;
408     	force_sig_info(SIGTRAP, &info, current);
409     #ifdef DEBUG_SPARC_BREAKPOINT
410     	printk ("TRAP: Returning to space: PC=%lx nPC=%lx\n", regs->tpc, regs->tnpc);
411     #endif
412     }
413     
414     extern void check_pending(int signum);
415     
416     asmlinkage int sys_getdomainname(char *name, int len)
417     {
418             int nlen;
419     	int err = -EFAULT;
420     
421      	down_read(&uts_sem);
422      	
423     	nlen = strlen(system_utsname.domainname) + 1;
424     
425             if (nlen < len)
426                     len = nlen;
427     	if(len > __NEW_UTS_LEN)
428     		goto done;
429     	if(copy_to_user(name, system_utsname.domainname, len))
430     		goto done;
431     	err = 0;
432     done:
433     	up_read(&uts_sem);
434     	return err;
435     }
436     
437     /* only AP+ systems have sys_aplib */
438     asmlinkage int sys_aplib(void)
439     {
440     	return -ENOSYS;
441     }
442     
443     asmlinkage int solaris_syscall(struct pt_regs *regs)
444     {
445     	static int count = 0;
446     
447     	regs->tpc = regs->tnpc;
448     	regs->tnpc += 4;
449     	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
450     		regs->tpc &= 0xffffffff;
451     		regs->tnpc &= 0xffffffff;
452     	}
453     	if(++count <= 5) {
454     		printk ("For Solaris binary emulation you need solaris module loaded\n");
455     		show_regs (regs);
456     	}
457     	send_sig(SIGSEGV, current, 1);
458     
459     	return -ENOSYS;
460     }
461     
462     #ifndef CONFIG_SUNOS_EMUL
463     asmlinkage int sunos_syscall(struct pt_regs *regs)
464     {
465     	static int count = 0;
466     
467     	regs->tpc = regs->tnpc;
468     	regs->tnpc += 4;
469     	if ((current->thread.flags & SPARC_FLAG_32BIT) != 0) {
470     		regs->tpc &= 0xffffffff;
471     		regs->tnpc &= 0xffffffff;
472     	}
473     	if(++count <= 20)
474     		printk ("SunOS binary emulation not compiled in\n");
475     	force_sig(SIGSEGV, current);
476     
477     	return -ENOSYS;
478     }
479     #endif
480     
481     asmlinkage int sys_utrap_install(utrap_entry_t type, utrap_handler_t new_p,
482     				 utrap_handler_t new_d,
483     				 utrap_handler_t *old_p, utrap_handler_t *old_d)
484     {
485     	if (type < UT_INSTRUCTION_EXCEPTION || type > UT_TRAP_INSTRUCTION_31)
486     		return -EINVAL;
487     	if (new_p == (utrap_handler_t)(long)UTH_NOCHANGE) {
488     		if (old_p) {
489     			if (!current->thread.utraps) {
490     				if (put_user(NULL, old_p))
491     					return -EFAULT;
492     			} else {
493     				if (put_user((utrap_handler_t)(current->thread.utraps[type]), old_p))
494     					return -EFAULT;
495     			}
496     		}
497     		if (old_d) {
498     			if (put_user(NULL, old_d))
499     				return -EFAULT;
500     		}
501     		return 0;
502     	}
503     	if (!current->thread.utraps) {
504     		current->thread.utraps =
505     			kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL);
506     		if (!current->thread.utraps) return -ENOMEM;
507     		current->thread.utraps[0] = 1;
508     		memset(current->thread.utraps+1, 0, UT_TRAP_INSTRUCTION_31*sizeof(long));
509     	} else {
510     		if ((utrap_handler_t)current->thread.utraps[type] != new_p &&
511     		    current->thread.utraps[0] > 1) {
512     			long *p = current->thread.utraps;
513     
514     			current->thread.utraps =
515     				kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long),
516     					GFP_KERNEL);
517     			if (!current->thread.utraps) {
518     				current->thread.utraps = p;
519     				return -ENOMEM;
520     			}
521     			p[0]--;
522     			current->thread.utraps[0] = 1;
523     			memcpy(current->thread.utraps+1, p+1,
524     			       UT_TRAP_INSTRUCTION_31*sizeof(long));
525     		}
526     	}
527     	if (old_p) {
528     		if (put_user((utrap_handler_t)(current->thread.utraps[type]), old_p))
529     			return -EFAULT;
530     	}
531     	if (old_d) {
532     		if (put_user(NULL, old_d))
533     			return -EFAULT;
534     	}
535     	current->thread.utraps[type] = (long)new_p;
536     
537     	return 0;
538     }
539     
540     long sparc_memory_ordering(unsigned long model, struct pt_regs *regs)
541     {
542     	if (model >= 3)
543     		return -EINVAL;
544     	regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14);
545     	return 0;
546     }
547     
548     asmlinkage int
549     sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
550     		 void *restorer, size_t sigsetsize)
551     {
552     	struct k_sigaction new_ka, old_ka;
553     	int ret;
554     
555     	/* XXX: Don't preclude handling different sized sigset_t's.  */
556     	if (sigsetsize != sizeof(sigset_t))
557     		return -EINVAL;
558     
559     	if (act) {
560     		new_ka.ka_restorer = restorer;
561     		if (copy_from_user(&new_ka.sa, act, sizeof(*act)))
562     			return -EFAULT;
563     	}
564     
565     	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
566     
567     	if (!ret && oact) {
568     		if (copy_to_user(oact, &old_ka.sa, sizeof(*oact)))
569     			return -EFAULT;
570     	}
571     
572     	return ret;
573     }
574     
575     /* Invoked by rtrap code to update performance counters in
576      * user space.
577      */
578     asmlinkage void
579     update_perfctrs(void)
580     {
581     	unsigned long pic, tmp;
582     
583     	read_pic(pic);
584     	tmp = (current->thread.kernel_cntd0 += (unsigned int)pic);
585     	__put_user(tmp, current->thread.user_cntd0);
586     	tmp = (current->thread.kernel_cntd1 += (pic >> 32));
587     	__put_user(tmp, current->thread.user_cntd1);
588     	reset_pic();
589     }
590     
591     asmlinkage int
592     sys_perfctr(int opcode, unsigned long arg0, unsigned long arg1, unsigned long arg2)
593     {
594     	int err = 0;
595     
596     	switch(opcode) {
597     	case PERFCTR_ON:
598     		current->thread.pcr_reg = arg2;
599     		current->thread.user_cntd0 = (u64 *) arg0;
600     		current->thread.user_cntd1 = (u64 *) arg1;
601     		current->thread.kernel_cntd0 =
602     			current->thread.kernel_cntd1 = 0;
603     		write_pcr(arg2);
604     		reset_pic();
605     		current->thread.flags |= SPARC_FLAG_PERFCTR;
606     		break;
607     
608     	case PERFCTR_OFF:
609     		err = -EINVAL;
610     		if ((current->thread.flags & SPARC_FLAG_PERFCTR) != 0) {
611     			current->thread.user_cntd0 =
612     				current->thread.user_cntd1 = NULL;
613     			current->thread.pcr_reg = 0;
614     			write_pcr(0);
615     			current->thread.flags &= ~(SPARC_FLAG_PERFCTR);
616     			err = 0;
617     		}
618     		break;
619     
620     	case PERFCTR_READ: {
621     		unsigned long pic, tmp;
622     
623     		if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
624     			err = -EINVAL;
625     			break;
626     		}
627     		read_pic(pic);
628     		tmp = (current->thread.kernel_cntd0 += (unsigned int)pic);
629     		err |= __put_user(tmp, current->thread.user_cntd0);
630     		tmp = (current->thread.kernel_cntd1 += (pic >> 32));
631     		err |= __put_user(tmp, current->thread.user_cntd1);
632     		reset_pic();
633     		break;
634     	}
635     
636     	case PERFCTR_CLRPIC:
637     		if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
638     			err = -EINVAL;
639     			break;
640     		}
641     		current->thread.kernel_cntd0 =
642     			current->thread.kernel_cntd1 = 0;
643     		reset_pic();
644     		break;
645     
646     	case PERFCTR_SETPCR: {
647     		u64 *user_pcr = (u64 *)arg0;
648     		if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
649     			err = -EINVAL;
650     			break;
651     		}
652     		err |= __get_user(current->thread.pcr_reg, user_pcr);
653     		write_pcr(current->thread.pcr_reg);
654     		current->thread.kernel_cntd0 =
655     			current->thread.kernel_cntd1 = 0;
656     		reset_pic();
657     		break;
658     	}
659     
660     	case PERFCTR_GETPCR: {
661     		u64 *user_pcr = (u64 *)arg0;
662     		if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
663     			err = -EINVAL;
664     			break;
665     		}
666     		err |= __put_user(current->thread.pcr_reg, user_pcr);
667     		break;
668     	}
669     
670     	default:
671     		err = -EINVAL;
672     		break;
673     	};
674     	return err;
675     }
676