Merge with 2.4.0-test3-pre7.
[linux-2.6/linux-mips.git] / arch / sparc64 / kernel / sys_sparc.c
blobec3042398c0a6ef406b30d529cdd4458531a7e17
1 /* $Id: sys_sparc.c,v 1.43 2000/07/06 01:41:29 davem Exp $
2 * linux/arch/sparc64/kernel/sys_sparc.c
4 * This file contains various random system calls that
5 * have a non-standard calling sequence on the Linux/sparc
6 * platform.
7 */
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/malloc.h>
25 #include <linux/ipc.h>
26 #include <linux/personality.h>
28 #include <asm/uaccess.h>
29 #include <asm/ipc.h>
30 #include <asm/utrap.h>
31 #include <asm/perfctr.h>
33 /* #define DEBUG_UNIMP_SYSCALL */
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.
38 asmlinkage unsigned long sys_getpagesize(void)
40 return PAGE_SIZE;
43 unsigned long get_unmapped_area(unsigned long addr, unsigned long len)
45 struct vm_area_struct * vmm;
46 unsigned long task_size = TASK_SIZE;
48 if (current->thread.flags & SPARC_FLAG_32BIT)
49 task_size = 0xf0000000UL;
50 if (len > task_size || len > -PAGE_OFFSET)
51 return 0;
52 if (!addr)
53 addr = TASK_UNMAPPED_BASE;
54 addr = PAGE_ALIGN(addr);
56 task_size -= len;
58 for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
59 /* At this point: (!vmm || addr < vmm->vm_end). */
60 if (addr < PAGE_OFFSET && -PAGE_OFFSET - len < addr) {
61 addr = PAGE_OFFSET;
62 vmm = find_vma(current->mm, PAGE_OFFSET);
64 if (task_size < addr)
65 return 0;
66 if (!vmm || addr + len <= vmm->vm_start)
67 return addr;
68 addr = vmm->vm_end;
72 extern asmlinkage unsigned long sys_brk(unsigned long brk);
74 asmlinkage unsigned long sparc_brk(unsigned long brk)
76 /* People could try to be nasty and use ta 0x6d in 32bit programs */
77 if ((current->thread.flags & SPARC_FLAG_32BIT) &&
78 brk >= 0xf0000000UL)
79 return current->mm->brk;
81 if ((current->mm->brk & PAGE_OFFSET) != (brk & PAGE_OFFSET))
82 return current->mm->brk;
83 return sys_brk(brk);
87 * sys_pipe() is the normal C calling standard for creating
88 * a pipe. It's not the way unix traditionally does this, though.
90 asmlinkage int sparc_pipe(struct pt_regs *regs)
92 int fd[2];
93 int error;
95 error = do_pipe(fd);
96 if (error)
97 goto out;
98 regs->u_regs[UREG_I1] = fd[1];
99 error = fd[0];
100 out:
101 return error;
105 * sys_ipc() is the de-multiplexer for the SysV IPC calls..
107 * This is really horribly ugly.
110 asmlinkage int sys_ipc (unsigned call, int first, int second, unsigned long third, void *ptr, long fifth)
112 int err;
114 /* No need for backward compatibility. We can start fresh... */
116 if (call <= SEMCTL)
117 switch (call) {
118 case SEMOP:
119 err = sys_semop (first, (struct sembuf *)ptr, second);
120 goto out;
121 case SEMGET:
122 err = sys_semget (first, second, (int)third);
123 goto out;
124 case SEMCTL: {
125 union semun fourth;
126 err = -EINVAL;
127 if (!ptr)
128 goto out;
129 err = -EFAULT;
130 if(get_user(fourth.__pad, (void **)ptr))
131 goto out;
132 err = sys_semctl (first, second | IPC_64, (int)third, fourth);
133 goto out;
135 default:
136 err = -EINVAL;
137 goto out;
139 if (call <= MSGCTL)
140 switch (call) {
141 case MSGSND:
142 err = sys_msgsnd (first, (struct msgbuf *) ptr,
143 second, (int)third);
144 goto out;
145 case MSGRCV:
146 err = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, (int)third);
147 goto out;
148 case MSGGET:
149 err = sys_msgget ((key_t) first, second);
150 goto out;
151 case MSGCTL:
152 err = sys_msgctl (first, second | IPC_64, (struct msqid_ds *) ptr);
153 goto out;
154 default:
155 err = -EINVAL;
156 goto out;
158 if (call <= SHMCTL)
159 switch (call) {
160 case SHMAT:
161 err = sys_shmat (first, (char *) ptr, second, (ulong *) third);
162 goto out;
163 case SHMDT:
164 err = sys_shmdt ((char *)ptr);
165 goto out;
166 case SHMGET:
167 err = sys_shmget (first, second, (int)third);
168 goto out;
169 case SHMCTL:
170 err = sys_shmctl (first, second | IPC_64, (struct shmid_ds *) ptr);
171 goto out;
172 default:
173 err = -EINVAL;
174 goto out;
176 else
177 err = -EINVAL;
178 out:
179 return err;
182 extern asmlinkage int sys_newuname(struct new_utsname * name);
184 asmlinkage int sparc64_newuname(struct new_utsname * name)
186 int ret = sys_newuname(name);
188 if (current->personality == PER_LINUX32 && !ret) {
189 ret = copy_to_user(name->machine, "sparc\0\0", 8);
191 return ret;
194 extern asmlinkage long sys_personality(unsigned long);
196 asmlinkage int sparc64_personality(unsigned long personality)
198 int ret;
199 if (current->personality == PER_LINUX32 && personality == PER_LINUX)
200 personality = PER_LINUX32;
201 ret = sys_personality(personality);
202 if (ret == PER_LINUX32)
203 ret = PER_LINUX;
204 return ret;
207 /* Linux version of mmap */
208 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
209 unsigned long prot, unsigned long flags, unsigned long fd,
210 unsigned long off)
212 struct file * file = NULL;
213 unsigned long retval = -EBADF;
215 if (!(flags & MAP_ANONYMOUS)) {
216 file = fget(fd);
217 if (!file)
218 goto out;
220 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
221 len = PAGE_ALIGN(len);
222 retval = -EINVAL;
224 if (current->thread.flags & SPARC_FLAG_32BIT) {
225 if (len > 0xf0000000UL ||
226 ((flags & MAP_FIXED) && addr > 0xf0000000UL - len))
227 goto out_putf;
228 } else {
229 if (len > -PAGE_OFFSET ||
230 ((flags & MAP_FIXED) &&
231 addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
232 goto out_putf;
235 down(&current->mm->mmap_sem);
236 retval = do_mmap(file, addr, len, prot, flags, off);
237 up(&current->mm->mmap_sem);
239 out_putf:
240 if (file)
241 fput(file);
242 out:
243 return retval;
246 asmlinkage long sys64_munmap(unsigned long addr, size_t len)
248 long ret;
250 if (len > -PAGE_OFFSET ||
251 (addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
252 return -EINVAL;
253 down(&current->mm->mmap_sem);
254 ret = do_munmap(current->mm, addr, len);
255 up(&current->mm->mmap_sem);
256 return ret;
259 extern unsigned long do_mremap(unsigned long addr,
260 unsigned long old_len, unsigned long new_len,
261 unsigned long flags, unsigned long new_addr);
263 asmlinkage unsigned long sys64_mremap(unsigned long addr,
264 unsigned long old_len, unsigned long new_len,
265 unsigned long flags, unsigned long new_addr)
267 unsigned long ret = -EINVAL;
268 if (current->thread.flags & SPARC_FLAG_32BIT)
269 goto out;
270 if (old_len > -PAGE_OFFSET || new_len > -PAGE_OFFSET)
271 goto out;
272 if (addr < PAGE_OFFSET && addr + old_len > -PAGE_OFFSET)
273 goto out;
274 down(&current->mm->mmap_sem);
275 if (flags & MREMAP_FIXED) {
276 if (new_addr < PAGE_OFFSET &&
277 new_addr + new_len > -PAGE_OFFSET)
278 goto out_sem;
279 } else if (addr < PAGE_OFFSET && addr + new_len > -PAGE_OFFSET) {
280 ret = -ENOMEM;
281 if (!(flags & MREMAP_MAYMOVE))
282 goto out_sem;
283 new_addr = get_unmapped_area (addr, new_len);
284 if (!new_addr)
285 goto out_sem;
286 flags |= MREMAP_FIXED;
288 ret = do_mremap(addr, old_len, new_len, flags, new_addr);
289 out_sem:
290 up(&current->mm->mmap_sem);
291 out:
292 return ret;
295 /* we come to here via sys_nis_syscall so it can setup the regs argument */
296 asmlinkage unsigned long
297 c_sys_nis_syscall (struct pt_regs *regs)
299 static int count=0;
301 /* Don't make the system unusable, if someone goes stuck */
302 if (count++ > 5) return -ENOSYS;
303 lock_kernel();
304 printk ("Unimplemented SPARC system call %ld\n",regs->u_regs[1]);
305 #ifdef DEBUG_UNIMP_SYSCALL
306 show_regs (regs);
307 #endif
308 unlock_kernel();
309 return -ENOSYS;
312 /* #define DEBUG_SPARC_BREAKPOINT */
314 asmlinkage void
315 sparc_breakpoint (struct pt_regs *regs)
317 siginfo_t info;
319 lock_kernel();
320 #ifdef DEBUG_SPARC_BREAKPOINT
321 printk ("TRAP: Entering kernel PC=%lx, nPC=%lx\n", regs->tpc, regs->tnpc);
322 #endif
323 info.si_signo = SIGTRAP;
324 info.si_errno = 0;
325 info.si_code = TRAP_BRKPT;
326 info.si_addr = (void *)regs->tpc;
327 info.si_trapno = 0;
328 force_sig_info(SIGTRAP, &info, current);
329 #ifdef DEBUG_SPARC_BREAKPOINT
330 printk ("TRAP: Returning to space: PC=%lx nPC=%lx\n", regs->tpc, regs->tnpc);
331 #endif
332 unlock_kernel();
335 extern void check_pending(int signum);
337 asmlinkage int sys_getdomainname(char *name, int len)
339 int nlen;
340 int err = -EFAULT;
342 down_read(&uts_sem);
344 nlen = strlen(system_utsname.domainname) + 1;
346 if (nlen < len)
347 len = nlen;
348 if(len > __NEW_UTS_LEN)
349 goto done;
350 if(copy_to_user(name, system_utsname.domainname, len))
351 goto done;
352 err = 0;
353 done:
354 up_read(&uts_sem);
355 return err;
358 /* only AP+ systems have sys_aplib */
359 asmlinkage int sys_aplib(void)
361 return -ENOSYS;
364 asmlinkage int solaris_syscall(struct pt_regs *regs)
366 static int count = 0;
367 lock_kernel();
368 regs->tpc = regs->tnpc;
369 regs->tnpc += 4;
370 if(++count <= 5) {
371 printk ("For Solaris binary emulation you need solaris module loaded\n");
372 show_regs (regs);
374 send_sig(SIGSEGV, current, 1);
375 unlock_kernel();
376 return -ENOSYS;
379 #ifndef CONFIG_SUNOS_EMUL
380 asmlinkage int sunos_syscall(struct pt_regs *regs)
382 static int count = 0;
383 lock_kernel();
384 regs->tpc = regs->tnpc;
385 regs->tnpc += 4;
386 if(++count <= 20)
387 printk ("SunOS binary emulation not compiled in\n");
388 force_sig(SIGSEGV, current);
389 unlock_kernel();
390 return -ENOSYS;
392 #endif
394 asmlinkage int sys_utrap_install(utrap_entry_t type, utrap_handler_t new_p,
395 utrap_handler_t new_d,
396 utrap_handler_t *old_p, utrap_handler_t *old_d)
398 if (type < UT_INSTRUCTION_EXCEPTION || type > UT_TRAP_INSTRUCTION_31)
399 return -EINVAL;
400 if (new_p == (utrap_handler_t)(long)UTH_NOCHANGE) {
401 if (old_p) {
402 if (!current->thread.utraps)
403 put_user_ret(NULL, old_p, -EFAULT);
404 else
405 put_user_ret((utrap_handler_t)(current->thread.utraps[type]), old_p, -EFAULT);
407 if (old_d)
408 put_user_ret(NULL, old_d, -EFAULT);
409 return 0;
411 lock_kernel();
412 if (!current->thread.utraps) {
413 current->thread.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL);
414 if (!current->thread.utraps) return -ENOMEM;
415 current->thread.utraps[0] = 1;
416 memset(current->thread.utraps+1, 0, UT_TRAP_INSTRUCTION_31*sizeof(long));
417 } else {
418 if ((utrap_handler_t)current->thread.utraps[type] != new_p && current->thread.utraps[0] > 1) {
419 long *p = current->thread.utraps;
421 current->thread.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL);
422 if (!current->thread.utraps) {
423 current->thread.utraps = p;
424 return -ENOMEM;
426 p[0]--;
427 current->thread.utraps[0] = 1;
428 memcpy(current->thread.utraps+1, p+1, UT_TRAP_INSTRUCTION_31*sizeof(long));
431 if (old_p)
432 put_user_ret((utrap_handler_t)(current->thread.utraps[type]), old_p, -EFAULT);
433 if (old_d)
434 put_user_ret(NULL, old_d, -EFAULT);
435 current->thread.utraps[type] = (long)new_p;
436 unlock_kernel();
437 return 0;
440 long sparc_memory_ordering(unsigned long model, struct pt_regs *regs)
442 if (model >= 3)
443 return -EINVAL;
444 regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14);
445 return 0;
448 asmlinkage int
449 sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact,
450 void *restorer, size_t sigsetsize)
452 struct k_sigaction new_ka, old_ka;
453 int ret;
455 /* XXX: Don't preclude handling different sized sigset_t's. */
456 if (sigsetsize != sizeof(sigset_t))
457 return -EINVAL;
459 if (act) {
460 new_ka.ka_restorer = restorer;
461 if (copy_from_user(&new_ka.sa, act, sizeof(*act)))
462 return -EFAULT;
465 ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
467 if (!ret && oact) {
468 if (copy_to_user(oact, &old_ka.sa, sizeof(*oact)))
469 return -EFAULT;
472 return ret;
475 /* Invoked by rtrap code to update performance counters in
476 * user space.
478 asmlinkage void
479 update_perfctrs(void)
481 unsigned long pic, tmp;
483 read_pic(pic);
484 tmp = (current->thread.kernel_cntd0 += (unsigned int)pic);
485 __put_user(tmp, current->thread.user_cntd0);
486 tmp = (current->thread.kernel_cntd1 += (pic >> 32));
487 __put_user(tmp, current->thread.user_cntd1);
488 reset_pic();
491 asmlinkage int
492 sys_perfctr(int opcode, unsigned long arg0, unsigned long arg1, unsigned long arg2)
494 int err = 0;
496 switch(opcode) {
497 case PERFCTR_ON:
498 current->thread.pcr_reg = arg2;
499 current->thread.user_cntd0 = (u64 *) arg0;
500 current->thread.user_cntd1 = (u64 *) arg1;
501 current->thread.kernel_cntd0 =
502 current->thread.kernel_cntd1 = 0;
503 write_pcr(arg2);
504 reset_pic();
505 current->thread.flags |= SPARC_FLAG_PERFCTR;
506 break;
508 case PERFCTR_OFF:
509 err = -EINVAL;
510 if ((current->thread.flags & SPARC_FLAG_PERFCTR) != 0) {
511 current->thread.user_cntd0 =
512 current->thread.user_cntd1 = NULL;
513 current->thread.pcr_reg = 0;
514 write_pcr(0);
515 current->thread.flags &= ~(SPARC_FLAG_PERFCTR);
516 err = 0;
518 break;
520 case PERFCTR_READ: {
521 unsigned long pic, tmp;
523 if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
524 err = -EINVAL;
525 break;
527 read_pic(pic);
528 tmp = (current->thread.kernel_cntd0 += (unsigned int)pic);
529 err |= __put_user(tmp, current->thread.user_cntd0);
530 tmp = (current->thread.kernel_cntd1 += (pic >> 32));
531 err |= __put_user(tmp, current->thread.user_cntd1);
532 reset_pic();
533 break;
536 case PERFCTR_CLRPIC:
537 if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
538 err = -EINVAL;
539 break;
541 current->thread.kernel_cntd0 =
542 current->thread.kernel_cntd1 = 0;
543 reset_pic();
544 break;
546 case PERFCTR_SETPCR: {
547 u64 *user_pcr = (u64 *)arg0;
548 if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
549 err = -EINVAL;
550 break;
552 err |= __get_user(current->thread.pcr_reg, user_pcr);
553 write_pcr(current->thread.pcr_reg);
554 current->thread.kernel_cntd0 =
555 current->thread.kernel_cntd1 = 0;
556 reset_pic();
557 break;
560 case PERFCTR_GETPCR: {
561 u64 *user_pcr = (u64 *)arg0;
562 if (!(current->thread.flags & SPARC_FLAG_PERFCTR)) {
563 err = -EINVAL;
564 break;
566 err |= __put_user(current->thread.pcr_reg, user_pcr);
567 break;
570 default:
571 err = -EINVAL;
572 break;
574 return err;