1/* $Id: sys_sparc.c,v 1.1.1.1 2007/08/03 18:52:17 Exp $ 2 * linux/arch/sparc/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/errno.h> 10#include <linux/types.h> 11#include <linux/sched.h> 12#include <linux/mm.h> 13#include <linux/fs.h> 14#include <linux/file.h> 15#include <linux/sem.h> 16#include <linux/msg.h> 17#include <linux/shm.h> 18#include <linux/stat.h> 19#include <linux/syscalls.h> 20#include <linux/mman.h> 21#include <linux/utsname.h> 22#include <linux/smp.h> 23#include <linux/smp_lock.h> 24 25#include <asm/uaccess.h> 26#include <asm/ipc.h> 27#include <asm/unistd.h> 28 29/* #define DEBUG_UNIMP_SYSCALL */ 30 31asmlinkage unsigned long sys_getpagesize(void) 32{ 33 return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */ 34} 35 36#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1)) 37 38unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) 39{ 40 struct vm_area_struct * vmm; 41 42 if (flags & MAP_FIXED) { 43 /* We do not accept a shared mapping if it would violate 44 * cache aliasing constraints. 45 */ 46 if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1))) 47 return -EINVAL; 48 return addr; 49 } 50 51 /* See asm-sparc/uaccess.h */ 52 if (len > TASK_SIZE - PAGE_SIZE) 53 return -ENOMEM; 54 if (ARCH_SUN4C_SUN4 && len > 0x20000000) 55 return -ENOMEM; 56 if (!addr) 57 addr = TASK_UNMAPPED_BASE; 58 59 if (flags & MAP_SHARED) 60 addr = COLOUR_ALIGN(addr); 61 else 62 addr = PAGE_ALIGN(addr); 63 64 for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { 65 /* At this point: (!vmm || addr < vmm->vm_end). */ 66 if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) { 67 addr = PAGE_OFFSET; 68 vmm = find_vma(current->mm, PAGE_OFFSET); 69 } 70 if (TASK_SIZE - PAGE_SIZE - len < addr) 71 return -ENOMEM; 72 if (!vmm || addr + len <= vmm->vm_start) 73 return addr; 74 addr = vmm->vm_end; 75 if (flags & MAP_SHARED) 76 addr = COLOUR_ALIGN(addr); 77 } 78} 79 80asmlinkage unsigned long sparc_brk(unsigned long brk) 81{ 82 if(ARCH_SUN4C_SUN4) { 83 if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000)) 84 return current->mm->brk; 85 } 86 return sys_brk(brk); 87} 88 89/* 90 * sys_pipe() is the normal C calling standard for creating 91 * a pipe. It's not the way unix traditionally does this, though. 92 */ 93asmlinkage int sparc_pipe(struct pt_regs *regs) 94{ 95 int fd[2]; 96 int error; 97 98 error = do_pipe(fd); 99 if (error) 100 goto out; 101 regs->u_regs[UREG_I1] = fd[1]; 102 error = fd[0]; 103out: 104 return error; 105} 106 107/* 108 * sys_ipc() is the de-multiplexer for the SysV IPC calls.. 109 * 110 * This is really horribly ugly. 111 */ 112 113asmlinkage int sys_ipc (uint call, int first, int second, int third, void __user *ptr, long fifth) 114{ 115 int version, err; 116 117 version = call >> 16; /* hack for backward compatibility */ 118 call &= 0xffff; 119 120 if (call <= SEMCTL) 121 switch (call) { 122 case SEMOP: 123 err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); 124 goto out; 125 case SEMTIMEDOP: 126 err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, (const struct timespec __user *) fifth); 127 goto out; 128 case SEMGET: 129 err = sys_semget (first, second, third); 130 goto out; 131 case SEMCTL: { 132 union semun fourth; 133 err = -EINVAL; 134 if (!ptr) 135 goto out; 136 err = -EFAULT; 137 if (get_user(fourth.__pad, 138 (void __user * __user *)ptr)) 139 goto out; 140 err = sys_semctl (first, second, third, fourth); 141 goto out; 142 } 143 default: 144 err = -ENOSYS; 145 goto out; 146 } 147 if (call <= MSGCTL) 148 switch (call) { 149 case MSGSND: 150 err = sys_msgsnd (first, (struct msgbuf __user *) ptr, 151 second, third); 152 goto out; 153 case MSGRCV: 154 switch (version) { 155 case 0: { 156 struct ipc_kludge tmp; 157 err = -EINVAL; 158 if (!ptr) 159 goto out; 160 err = -EFAULT; 161 if (copy_from_user(&tmp, (struct ipc_kludge __user *) ptr, sizeof (tmp))) 162 goto out; 163 err = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); 164 goto out; 165 } 166 case 1: default: 167 err = sys_msgrcv (first, 168 (struct msgbuf __user *) ptr, 169 second, fifth, third); 170 goto out; 171 } 172 case MSGGET: 173 err = sys_msgget ((key_t) first, second); 174 goto out; 175 case MSGCTL: 176 err = sys_msgctl (first, second, (struct msqid_ds __user *) ptr); 177 goto out; 178 default: 179 err = -ENOSYS; 180 goto out; 181 } 182 if (call <= SHMCTL) 183 switch (call) { 184 case SHMAT: 185 switch (version) { 186 case 0: default: { 187 ulong raddr; 188 err = do_shmat (first, (char __user *) ptr, second, &raddr); 189 if (err) 190 goto out; 191 err = -EFAULT; 192 if (put_user (raddr, (ulong __user *) third)) 193 goto out; 194 err = 0; 195 goto out; 196 } 197 case 1: /* iBCS2 emulator entry point */ 198 err = -EINVAL; 199 goto out; 200 } 201 case SHMDT: 202 err = sys_shmdt ((char __user *)ptr); 203 goto out; 204 case SHMGET: 205 err = sys_shmget (first, second, third); 206 goto out; 207 case SHMCTL: 208 err = sys_shmctl (first, second, (struct shmid_ds __user *) ptr); 209 goto out; 210 default: 211 err = -ENOSYS; 212 goto out; 213 } 214 else 215 err = -ENOSYS; 216out: 217 return err; 218} 219 220int sparc_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) 221{ 222 if (ARCH_SUN4C_SUN4 && 223 (len > 0x20000000 || 224 ((flags & MAP_FIXED) && 225 addr < 0xe0000000 && addr + len > 0x20000000))) 226 return -EINVAL; 227 228 /* See asm-sparc/uaccess.h */ 229 if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE) 230 return -EINVAL; 231 232 return 0; 233} 234 235/* Linux version of mmap */ 236static unsigned long do_mmap2(unsigned long addr, unsigned long len, 237 unsigned long prot, unsigned long flags, unsigned long fd, 238 unsigned long pgoff) 239{ 240 struct file * file = NULL; 241 unsigned long retval = -EBADF; 242 243 if (!(flags & MAP_ANONYMOUS)) { 244 file = fget(fd); 245 if (!file) 246 goto out; 247 } 248 249 len = PAGE_ALIGN(len); 250 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 251 252 down_write(¤t->mm->mmap_sem); 253 retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 254 up_write(¤t->mm->mmap_sem); 255 256 if (file) 257 fput(file); 258out: 259 return retval; 260} 261 262asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len, 263 unsigned long prot, unsigned long flags, unsigned long fd, 264 unsigned long pgoff) 265{ 266 /* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE 267 we have. */ 268 return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12)); 269} 270 271asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, 272 unsigned long prot, unsigned long flags, unsigned long fd, 273 unsigned long off) 274{ 275 return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT); 276} 277 278long sparc_remap_file_pages(unsigned long start, unsigned long size, 279 unsigned long prot, unsigned long pgoff, 280 unsigned long flags) 281{ 282 /* This works on an existing mmap so we don't need to validate 283 * the range as that was done at the original mmap call. 284 */ 285 return sys_remap_file_pages(start, size, prot, 286 (pgoff >> (PAGE_SHIFT - 12)), flags); 287} 288 289extern unsigned long do_mremap(unsigned long addr, 290 unsigned long old_len, unsigned long new_len, 291 unsigned long flags, unsigned long new_addr); 292 293asmlinkage unsigned long sparc_mremap(unsigned long addr, 294 unsigned long old_len, unsigned long new_len, 295 unsigned long flags, unsigned long new_addr) 296{ 297 struct vm_area_struct *vma; 298 unsigned long ret = -EINVAL; 299 if (ARCH_SUN4C_SUN4) { 300 if (old_len > 0x20000000 || new_len > 0x20000000) 301 goto out; 302 if (addr < 0xe0000000 && addr + old_len > 0x20000000) 303 goto out; 304 } 305 if (old_len > TASK_SIZE - PAGE_SIZE || 306 new_len > TASK_SIZE - PAGE_SIZE) 307 goto out; 308 down_write(¤t->mm->mmap_sem); 309 if (flags & MREMAP_FIXED) { 310 if (ARCH_SUN4C_SUN4 && 311 new_addr < 0xe0000000 && 312 new_addr + new_len > 0x20000000) 313 goto out_sem; 314 if (new_addr + new_len > TASK_SIZE - PAGE_SIZE) 315 goto out_sem; 316 } else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 317 addr + new_len > 0x20000000) || 318 addr + new_len > TASK_SIZE - PAGE_SIZE) { 319 unsigned long map_flags = 0; 320 struct file *file = NULL; 321 322 ret = -ENOMEM; 323 if (!(flags & MREMAP_MAYMOVE)) 324 goto out_sem; 325 326 vma = find_vma(current->mm, addr); 327 if (vma) { 328 if (vma->vm_flags & VM_SHARED) 329 map_flags |= MAP_SHARED; 330 file = vma->vm_file; 331 } 332 333 new_addr = get_unmapped_area(file, addr, new_len, 334 vma ? vma->vm_pgoff : 0, 335 map_flags); 336 ret = new_addr; 337 if (new_addr & ~PAGE_MASK) 338 goto out_sem; 339 flags |= MREMAP_FIXED; 340 } 341 ret = do_mremap(addr, old_len, new_len, flags, new_addr); 342out_sem: 343 up_write(¤t->mm->mmap_sem); 344out: 345 return ret; 346} 347 348/* we come to here via sys_nis_syscall so it can setup the regs argument */ 349asmlinkage unsigned long 350c_sys_nis_syscall (struct pt_regs *regs) 351{ 352 static int count = 0; 353 354 if (count++ > 5) 355 return -ENOSYS; 356 printk ("%s[%d]: Unimplemented SPARC system call %d\n", 357 current->comm, current->pid, (int)regs->u_regs[1]); 358#ifdef DEBUG_UNIMP_SYSCALL 359 show_regs (regs); 360#endif 361 return -ENOSYS; 362} 363 364/* #define DEBUG_SPARC_BREAKPOINT */ 365 366asmlinkage void 367sparc_breakpoint (struct pt_regs *regs) 368{ 369 siginfo_t info; 370 371 lock_kernel(); 372#ifdef DEBUG_SPARC_BREAKPOINT 373 printk ("TRAP: Entering kernel PC=%x, nPC=%x\n", regs->pc, regs->npc); 374#endif 375 info.si_signo = SIGTRAP; 376 info.si_errno = 0; 377 info.si_code = TRAP_BRKPT; 378 info.si_addr = (void __user *)regs->pc; 379 info.si_trapno = 0; 380 force_sig_info(SIGTRAP, &info, current); 381 382#ifdef DEBUG_SPARC_BREAKPOINT 383 printk ("TRAP: Returning to space: PC=%x nPC=%x\n", regs->pc, regs->npc); 384#endif 385 unlock_kernel(); 386} 387 388asmlinkage int 389sparc_sigaction (int sig, const struct old_sigaction __user *act, 390 struct old_sigaction __user *oact) 391{ 392 struct k_sigaction new_ka, old_ka; 393 int ret; 394 395 if (sig < 0) { 396 current->thread.new_signal = 1; 397 sig = -sig; 398 } 399 400 if (act) { 401 unsigned long mask; 402 403 if (!access_ok(VERIFY_READ, act, sizeof(*act)) || 404 __get_user(new_ka.sa.sa_handler, &act->sa_handler) || 405 __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) 406 return -EFAULT; 407 __get_user(new_ka.sa.sa_flags, &act->sa_flags); 408 __get_user(mask, &act->sa_mask); 409 siginitset(&new_ka.sa.sa_mask, mask); 410 new_ka.ka_restorer = NULL; 411 } 412 413 ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); 414 415 if (!ret && oact) { 416 /* In the clone() case we could copy half consistent 417 * state to the user, however this could sleep and 418 * deadlock us if we held the signal lock on SMP. So for 419 * now I take the easy way out and do no locking. 420 */ 421 if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || 422 __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || 423 __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) 424 return -EFAULT; 425 __put_user(old_ka.sa.sa_flags, &oact->sa_flags); 426 __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); 427 } 428 429 return ret; 430} 431 432asmlinkage long 433sys_rt_sigaction(int sig, 434 const struct sigaction __user *act, 435 struct sigaction __user *oact, 436 void __user *restorer, 437 size_t sigsetsize) 438{ 439 struct k_sigaction new_ka, old_ka; 440 int ret; 441 442 if (sigsetsize != sizeof(sigset_t)) 443 return -EINVAL; 444 445 /* All tasks which use RT signals (effectively) use 446 * new style signals. 447 */ 448 current->thread.new_signal = 1; 449 450 if (act) { 451 new_ka.ka_restorer = restorer; 452 if (copy_from_user(&new_ka.sa, act, sizeof(*act))) 453 return -EFAULT; 454 } 455 456 ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); 457 458 if (!ret && oact) { 459 if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) 460 return -EFAULT; 461 } 462 463 return ret; 464} 465 466asmlinkage int sys_getdomainname(char __user *name, int len) 467{ 468 int nlen, err; 469 470 if (len < 0) 471 return -EINVAL; 472 473 down_read(&uts_sem); 474 475 nlen = strlen(utsname()->domainname) + 1; 476 err = -EINVAL; 477 if (nlen > len) 478 goto out; 479 480 err = -EFAULT; 481 if (!copy_to_user(name, utsname()->domainname, nlen)) 482 err = 0; 483 484out: 485 up_read(&uts_sem); 486 return err; 487} 488 489/* 490 * Do a system call from kernel instead of calling sys_execve so we 491 * end up with proper pt_regs. 492 */ 493int kernel_execve(const char *filename, char *const argv[], char *const envp[]) 494{ 495 long __res; 496 register long __g1 __asm__ ("g1") = __NR_execve; 497 register long __o0 __asm__ ("o0") = (long)(filename); 498 register long __o1 __asm__ ("o1") = (long)(argv); 499 register long __o2 __asm__ ("o2") = (long)(envp); 500 asm volatile ("t 0x10\n\t" 501 "bcc 1f\n\t" 502 "mov %%o0, %0\n\t" 503 "sub %%g0, %%o0, %0\n\t" 504 "1:\n\t" 505 : "=r" (__res), "=&r" (__o0) 506 : "1" (__o0), "r" (__o1), "r" (__o2), "r" (__g1) 507 : "cc"); 508 return __res; 509} 510