linux/arch/powerpc/kernel/syscalls.c
<<
>>
Prefs
   1/*
   2 *  Implementation of various system calls for Linux/PowerPC
   3 *
   4 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
   5 *
   6 * Derived from "arch/i386/kernel/sys_i386.c"
   7 * Adapted from the i386 version by Gary Thomas
   8 * Modified by Cort Dougan (cort@cs.nmt.edu)
   9 * and Paul Mackerras (paulus@cs.anu.edu.au).
  10 *
  11 * This file contains various random system calls that
  12 * have a non-standard calling sequence on the Linux/PPC
  13 * platform.
  14 *
  15 *  This program is free software; you can redistribute it and/or
  16 *  modify it under the terms of the GNU General Public License
  17 *  as published by the Free Software Foundation; either version
  18 *  2 of the License, or (at your option) any later version.
  19 *
  20 */
  21
  22#include <linux/errno.h>
  23#include <linux/sched.h>
  24#include <linux/syscalls.h>
  25#include <linux/mm.h>
  26#include <linux/fs.h>
  27#include <linux/smp.h>
  28#include <linux/sem.h>
  29#include <linux/msg.h>
  30#include <linux/shm.h>
  31#include <linux/stat.h>
  32#include <linux/mman.h>
  33#include <linux/sys.h>
  34#include <linux/ipc.h>
  35#include <linux/utsname.h>
  36#include <linux/file.h>
  37#include <linux/init.h>
  38#include <linux/personality.h>
  39
  40#include <asm/uaccess.h>
  41#include <asm/syscalls.h>
  42#include <asm/time.h>
  43#include <asm/unistd.h>
  44
  45/*
  46 * sys_ipc() is the de-multiplexer for the SysV IPC calls..
  47 *
  48 * This is really horribly ugly.
  49 */
  50int sys_ipc(uint call, int first, unsigned long second, long third,
  51            void __user *ptr, long fifth)
  52{
  53        int version, ret;
  54
  55        version = call >> 16; /* hack for backward compatibility */
  56        call &= 0xffff;
  57
  58        ret = -ENOSYS;
  59        switch (call) {
  60        case SEMOP:
  61                ret = sys_semtimedop(first, (struct sembuf __user *)ptr,
  62                                      (unsigned)second, NULL);
  63                break;
  64        case SEMTIMEDOP:
  65                ret = sys_semtimedop(first, (struct sembuf __user *)ptr,
  66                                      (unsigned)second,
  67                                      (const struct timespec __user *) fifth);
  68                break;
  69        case SEMGET:
  70                ret = sys_semget (first, (int)second, third);
  71                break;
  72        case SEMCTL: {
  73                union semun fourth;
  74
  75                ret = -EINVAL;
  76                if (!ptr)
  77                        break;
  78                if ((ret = get_user(fourth.__pad, (void __user * __user *)ptr)))
  79                        break;
  80                ret = sys_semctl(first, (int)second, third, fourth);
  81                break;
  82        }
  83        case MSGSND:
  84                ret = sys_msgsnd(first, (struct msgbuf __user *)ptr,
  85                                 (size_t)second, third);
  86                break;
  87        case MSGRCV:
  88                switch (version) {
  89                case 0: {
  90                        struct ipc_kludge tmp;
  91
  92                        ret = -EINVAL;
  93                        if (!ptr)
  94                                break;
  95                        if ((ret = copy_from_user(&tmp,
  96                                                (struct ipc_kludge __user *) ptr,
  97                                                sizeof (tmp)) ? -EFAULT : 0))
  98                                break;
  99                        ret = sys_msgrcv(first, tmp.msgp, (size_t) second,
 100                                          tmp.msgtyp, third);
 101                        break;
 102                }
 103                default:
 104                        ret = sys_msgrcv (first, (struct msgbuf __user *) ptr,
 105                                          (size_t)second, fifth, third);
 106                        break;
 107                }
 108                break;
 109        case MSGGET:
 110                ret = sys_msgget((key_t)first, (int)second);
 111                break;
 112        case MSGCTL:
 113                ret = sys_msgctl(first, (int)second,
 114                                  (struct msqid_ds __user *)ptr);
 115                break;
 116        case SHMAT: {
 117                ulong raddr;
 118                ret = do_shmat(first, (char __user *)ptr, (int)second, &raddr);
 119                if (ret)
 120                        break;
 121                ret = put_user(raddr, (ulong __user *) third);
 122                break;
 123        }
 124        case SHMDT:
 125                ret = sys_shmdt((char __user *)ptr);
 126                break;
 127        case SHMGET:
 128                ret = sys_shmget(first, (size_t)second, third);
 129                break;
 130        case SHMCTL:
 131                ret = sys_shmctl(first, (int)second,
 132                                 (struct shmid_ds __user *)ptr);
 133                break;
 134        }
 135
 136        return ret;
 137}
 138
 139static inline unsigned long do_mmap2(unsigned long addr, size_t len,
 140                        unsigned long prot, unsigned long flags,
 141                        unsigned long fd, unsigned long off, int shift)
 142{
 143        struct file * file = NULL;
 144        unsigned long ret = -EINVAL;
 145
 146        if (!arch_validate_prot(prot))
 147                goto out;
 148
 149        if (shift) {
 150                if (off & ((1 << shift) - 1))
 151                        goto out;
 152                off >>= shift;
 153        }
 154                
 155        ret = -EBADF;
 156        if (!(flags & MAP_ANONYMOUS)) {
 157                if (!(file = fget(fd)))
 158                        goto out;
 159        }
 160
 161        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 162
 163        down_write(&current->mm->mmap_sem);
 164        ret = do_mmap_pgoff(file, addr, len, prot, flags, off);
 165        up_write(&current->mm->mmap_sem);
 166        if (file)
 167                fput(file);
 168out:
 169        return ret;
 170}
 171
 172unsigned long sys_mmap2(unsigned long addr, size_t len,
 173                        unsigned long prot, unsigned long flags,
 174                        unsigned long fd, unsigned long pgoff)
 175{
 176        return do_mmap2(addr, len, prot, flags, fd, pgoff, PAGE_SHIFT-12);
 177}
 178
 179unsigned long sys_mmap(unsigned long addr, size_t len,
 180                       unsigned long prot, unsigned long flags,
 181                       unsigned long fd, off_t offset)
 182{
 183        return do_mmap2(addr, len, prot, flags, fd, offset, PAGE_SHIFT);
 184}
 185
 186#ifdef CONFIG_PPC32
 187/*
 188 * Due to some executables calling the wrong select we sometimes
 189 * get wrong args.  This determines how the args are being passed
 190 * (a single ptr to them all args passed) then calls
 191 * sys_select() with the appropriate args. -- Cort
 192 */
 193int
 194ppc_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp)
 195{
 196        if ( (unsigned long)n >= 4096 )
 197        {
 198                unsigned long __user *buffer = (unsigned long __user *)n;
 199                if (!access_ok(VERIFY_READ, buffer, 5*sizeof(unsigned long))
 200                    || __get_user(n, buffer)
 201                    || __get_user(inp, ((fd_set __user * __user *)(buffer+1)))
 202                    || __get_user(outp, ((fd_set  __user * __user *)(buffer+2)))
 203                    || __get_user(exp, ((fd_set  __user * __user *)(buffer+3)))
 204                    || __get_user(tvp, ((struct timeval  __user * __user *)(buffer+4))))
 205                        return -EFAULT;
 206        }
 207        return sys_select(n, inp, outp, exp, tvp);
 208}
 209#endif
 210
 211#ifdef CONFIG_PPC64
 212long ppc64_personality(unsigned long personality)
 213{
 214        long ret;
 215
 216        if (personality(current->personality) == PER_LINUX32
 217            && personality == PER_LINUX)
 218                personality = PER_LINUX32;
 219        ret = sys_personality(personality);
 220        if (ret == PER_LINUX32)
 221                ret = PER_LINUX;
 222        return ret;
 223}
 224#endif
 225
 226#ifdef CONFIG_PPC64
 227#define OVERRIDE_MACHINE    (personality(current->personality) == PER_LINUX32)
 228#else
 229#define OVERRIDE_MACHINE    0
 230#endif
 231
 232static inline int override_machine(char __user *mach)
 233{
 234        if (OVERRIDE_MACHINE) {
 235                /* change ppc64 to ppc */
 236                if (__put_user(0, mach+3) || __put_user(0, mach+4))
 237                        return -EFAULT;
 238        }
 239        return 0;
 240}
 241
 242long ppc_newuname(struct new_utsname __user * name)
 243{
 244        int err = 0;
 245
 246        down_read(&uts_sem);
 247        if (copy_to_user(name, utsname(), sizeof(*name)))
 248                err = -EFAULT;
 249        up_read(&uts_sem);
 250        if (!err)
 251                err = override_machine(name->machine);
 252        return err;
 253}
 254
 255int sys_uname(struct old_utsname __user *name)
 256{
 257        int err = 0;
 258        
 259        down_read(&uts_sem);
 260        if (copy_to_user(name, utsname(), sizeof(*name)))
 261                err = -EFAULT;
 262        up_read(&uts_sem);
 263        if (!err)
 264                err = override_machine(name->machine);
 265        return err;
 266}
 267
 268int sys_olduname(struct oldold_utsname __user *name)
 269{
 270        int error;
 271
 272        if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
 273                return -EFAULT;
 274  
 275        down_read(&uts_sem);
 276        error = __copy_to_user(&name->sysname, &utsname()->sysname,
 277                               __OLD_UTS_LEN);
 278        error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
 279        error |= __copy_to_user(&name->nodename, &utsname()->nodename,
 280                                __OLD_UTS_LEN);
 281        error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
 282        error |= __copy_to_user(&name->release, &utsname()->release,
 283                                __OLD_UTS_LEN);
 284        error |= __put_user(0, name->release + __OLD_UTS_LEN);
 285        error |= __copy_to_user(&name->version, &utsname()->version,
 286                                __OLD_UTS_LEN);
 287        error |= __put_user(0, name->version + __OLD_UTS_LEN);
 288        error |= __copy_to_user(&name->machine, &utsname()->machine,
 289                                __OLD_UTS_LEN);
 290        error |= override_machine(name->machine);
 291        up_read(&uts_sem);
 292
 293        return error? -EFAULT: 0;
 294}
 295
 296long ppc_fadvise64_64(int fd, int advice, u32 offset_high, u32 offset_low,
 297                      u32 len_high, u32 len_low)
 298{
 299        return sys_fadvise64(fd, (u64)offset_high << 32 | offset_low,
 300                             (u64)len_high << 32 | len_low, advice);
 301}
 302
 303void do_show_syscall(unsigned long r3, unsigned long r4, unsigned long r5,
 304                     unsigned long r6, unsigned long r7, unsigned long r8,
 305                     struct pt_regs *regs)
 306{
 307        printk("syscall %ld(%lx, %lx, %lx, %lx, %lx, %lx) regs=%p current=%p"
 308               " cpu=%d\n", regs->gpr[0], r3, r4, r5, r6, r7, r8, regs,
 309               current, smp_processor_id());
 310}
 311
 312void do_show_syscall_exit(unsigned long r3)
 313{
 314        printk(" -> %lx, current=%p cpu=%d\n", r3, current, smp_processor_id());
 315}
 316