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(¤t->mm->mmap_sem); 301 retval = do_mmap(file, addr, len, prot, flags, off); 302 up_write(¤t->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(¤t->mm->mmap_sem); 319 ret = do_munmap(current->mm, addr, len); 320 up_write(¤t->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(¤t->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(¤t->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