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