| 1 | 199 | simons | /*
 | 
      
         | 2 |  |  |  * linux/arch/arm/kernel/sys-arm.c
 | 
      
         | 3 |  |  |  *
 | 
      
         | 4 |  |  |  * Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
 | 
      
         | 5 |  |  |  * Copyright (C) 1995, 1996 Russell King.
 | 
      
         | 6 |  |  |  *
 | 
      
         | 7 |  |  |  * This file contains various random system calls that
 | 
      
         | 8 |  |  |  * have a non-standard calling sequence on the Linux/arm
 | 
      
         | 9 |  |  |  * platform.
 | 
      
         | 10 |  |  |  */
 | 
      
         | 11 |  |  |  
 | 
      
         | 12 |  |  | #include <linux/module.h>
 | 
      
         | 13 |  |  | #include <linux/errno.h>
 | 
      
         | 14 |  |  | #include <linux/sched.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 |  |  |  
 | 
      
         | 22 |  |  | #include <asm/segment.h>
 | 
      
         | 23 |  |  |  
 | 
      
         | 24 |  |  | /*
 | 
      
         | 25 |  |  |  * Constant strings used in inlined functions in header files
 | 
      
         | 26 |  |  |  */
 | 
      
         | 27 |  |  | /* proc/system.h */
 | 
      
         | 28 |  |  | const char xchg_str[] = "xchg";
 | 
      
         | 29 |  |  | /* arch/dma.h */
 | 
      
         | 30 |  |  | const char dma_str[] = "%s: dma %d not supported\n";
 | 
      
         | 31 |  |  |  
 | 
      
         | 32 |  |  | /*
 | 
      
         | 33 |  |  |  * sys_pipe() is the normal C calling standard for creating
 | 
      
         | 34 |  |  |  * a pipe. It's not the way unix tranditionally does this, though.
 | 
      
         | 35 |  |  |  */
 | 
      
         | 36 |  |  | asmlinkage int sys_pipe(unsigned long * fildes)
 | 
      
         | 37 |  |  | {
 | 
      
         | 38 |  |  |         int fd[2];
 | 
      
         | 39 |  |  |         int error;
 | 
      
         | 40 |  |  |  
 | 
      
         | 41 |  |  |         error = verify_area(VERIFY_WRITE,fildes,8);
 | 
      
         | 42 |  |  |         if (error)
 | 
      
         | 43 |  |  |                 return error;
 | 
      
         | 44 |  |  |         error = do_pipe(fd);
 | 
      
         | 45 |  |  |         if (error)
 | 
      
         | 46 |  |  |                 return error;
 | 
      
         | 47 |  |  |         put_fs_long(fd[0],0+fildes);
 | 
      
         | 48 |  |  |         put_fs_long(fd[1],1+fildes);
 | 
      
         | 49 |  |  |         return 0;
 | 
      
         | 50 |  |  | }
 | 
      
         | 51 |  |  |  
 | 
      
         | 52 |  |  | /*
 | 
      
         | 53 |  |  |  * Perform the select(nd, in, out, ex, tv) and mmap() system
 | 
      
         | 54 |  |  |  * calls. Linux/i386 didn't use to be able to handle more than
 | 
      
         | 55 |  |  |  * 4 system call parameters, so these system calls used a memory
 | 
      
         | 56 |  |  |  * block for parameter passing..
 | 
      
         | 57 |  |  |  */
 | 
      
         | 58 |  |  | asmlinkage int old_mmap(unsigned long *buffer)
 | 
      
         | 59 |  |  | {
 | 
      
         | 60 |  |  |         int error;
 | 
      
         | 61 |  |  |         unsigned long flags;
 | 
      
         | 62 |  |  |         struct file * file = NULL;
 | 
      
         | 63 |  |  |  
 | 
      
         | 64 |  |  |         error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
 | 
      
         | 65 |  |  |         if (error)
 | 
      
         | 66 |  |  |                 return error;
 | 
      
         | 67 |  |  |         flags = get_user(buffer+3);
 | 
      
         | 68 |  |  |         if (!(flags & MAP_ANONYMOUS)) {
 | 
      
         | 69 |  |  |                 unsigned long fd = get_user(buffer+4);
 | 
      
         | 70 |  |  |                 if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 | 
      
         | 71 |  |  |                         return -EBADF;
 | 
      
         | 72 |  |  |         }
 | 
      
         | 73 |  |  |         return do_mmap(file, get_user(buffer), get_user(buffer+1),
 | 
      
         | 74 |  |  |                        get_user(buffer+2), flags, get_user(buffer+5));
 | 
      
         | 75 |  |  | }
 | 
      
         | 76 |  |  |  
 | 
      
         | 77 |  |  |  
 | 
      
         | 78 |  |  | extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
 | 
      
         | 79 |  |  |  
 | 
      
         | 80 |  |  | asmlinkage int old_select(unsigned long *buffer)
 | 
      
         | 81 |  |  | {
 | 
      
         | 82 |  |  |         int n;
 | 
      
         | 83 |  |  |         fd_set *inp;
 | 
      
         | 84 |  |  |         fd_set *outp;
 | 
      
         | 85 |  |  |         fd_set *exp;
 | 
      
         | 86 |  |  |         struct timeval *tvp;
 | 
      
         | 87 |  |  |  
 | 
      
         | 88 |  |  |         n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long));
 | 
      
         | 89 |  |  |         if (n)
 | 
      
         | 90 |  |  |                 return n;
 | 
      
         | 91 |  |  |         n = get_user(buffer);
 | 
      
         | 92 |  |  |         inp = (fd_set *) get_user(buffer+1);
 | 
      
         | 93 |  |  |         outp = (fd_set *) get_user(buffer+2);
 | 
      
         | 94 |  |  |         exp = (fd_set *) get_user(buffer+3);
 | 
      
         | 95 |  |  |         tvp = (struct timeval *) get_user(buffer+4);
 | 
      
         | 96 |  |  |         return sys_select(n, inp, outp, exp, tvp);
 | 
      
         | 97 |  |  | }
 | 
      
         | 98 |  |  |  
 | 
      
         | 99 |  |  | /*
 | 
      
         | 100 |  |  |  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
 | 
      
         | 101 |  |  |  *
 | 
      
         | 102 |  |  |  * This is really horribly ugly.
 | 
      
         | 103 |  |  |  */
 | 
      
         | 104 |  |  | asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
 | 
      
         | 105 |  |  | {
 | 
      
         | 106 |  |  |         int version;
 | 
      
         | 107 |  |  |  
 | 
      
         | 108 |  |  |         version = call >> 16; /* hack for backward compatibility */
 | 
      
         | 109 |  |  |         call &= 0xffff;
 | 
      
         | 110 |  |  |  
 | 
      
         | 111 |  |  |         if (call <= SEMCTL)
 | 
      
         | 112 |  |  |                 switch (call) {
 | 
      
         | 113 |  |  |                 case SEMOP:
 | 
      
         | 114 |  |  |                         return sys_semop (first, (struct sembuf *)ptr, second);
 | 
      
         | 115 |  |  |                 case SEMGET:
 | 
      
         | 116 |  |  |                         return sys_semget (first, second, third);
 | 
      
         | 117 |  |  |                 case SEMCTL: {
 | 
      
         | 118 |  |  |                         union semun fourth;
 | 
      
         | 119 |  |  |                         int err;
 | 
      
         | 120 |  |  |                         if (!ptr)
 | 
      
         | 121 |  |  |                                 return -EINVAL;
 | 
      
         | 122 |  |  |                         if ((err = verify_area (VERIFY_READ, ptr, sizeof(long))))
 | 
      
         | 123 |  |  |                                 return err;
 | 
      
         | 124 |  |  |                         fourth.__pad = (void *) get_fs_long(ptr);
 | 
      
         | 125 |  |  |                         return sys_semctl (first, second, third, fourth);
 | 
      
         | 126 |  |  |                         }
 | 
      
         | 127 |  |  |                 default:
 | 
      
         | 128 |  |  |                         return -EINVAL;
 | 
      
         | 129 |  |  |                 }
 | 
      
         | 130 |  |  |         if (call <= MSGCTL)
 | 
      
         | 131 |  |  |                 switch (call) {
 | 
      
         | 132 |  |  |                 case MSGSND:
 | 
      
         | 133 |  |  |                         return sys_msgsnd (first, (struct msgbuf *) ptr,
 | 
      
         | 134 |  |  |                                            second, third);
 | 
      
         | 135 |  |  |                 case MSGRCV:
 | 
      
         | 136 |  |  |                         switch (version) {
 | 
      
         | 137 |  |  |                         case 0: {
 | 
      
         | 138 |  |  |                                 struct ipc_kludge tmp;
 | 
      
         | 139 |  |  |                                 int err;
 | 
      
         | 140 |  |  |                                 if (!ptr)
 | 
      
         | 141 |  |  |                                         return -EINVAL;
 | 
      
         | 142 |  |  |                                 if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp))))
 | 
      
         | 143 |  |  |                                         return err;
 | 
      
         | 144 |  |  |                                 memcpy_fromfs (&tmp,(struct ipc_kludge *) ptr,
 | 
      
         | 145 |  |  |                                                sizeof (tmp));
 | 
      
         | 146 |  |  |                                 return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third);
 | 
      
         | 147 |  |  |                                 }
 | 
      
         | 148 |  |  |                         case 1: default:
 | 
      
         | 149 |  |  |                                 return sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third);
 | 
      
         | 150 |  |  |                         }
 | 
      
         | 151 |  |  |                 case MSGGET:
 | 
      
         | 152 |  |  |                         return sys_msgget ((key_t) first, second);
 | 
      
         | 153 |  |  |                 case MSGCTL:
 | 
      
         | 154 |  |  |                         return sys_msgctl (first, second, (struct msqid_ds *) ptr);
 | 
      
         | 155 |  |  |                 default:
 | 
      
         | 156 |  |  |                         return -EINVAL;
 | 
      
         | 157 |  |  |                 }
 | 
      
         | 158 |  |  |         if (call <= SHMCTL)
 | 
      
         | 159 |  |  |                 switch (call) {
 | 
      
         | 160 |  |  |                 case SHMAT:
 | 
      
         | 161 |  |  |                         switch (version) {
 | 
      
         | 162 |  |  |                         case 0: default: {
 | 
      
         | 163 |  |  |                                 ulong raddr;
 | 
      
         | 164 |  |  |                                 int err;
 | 
      
         | 165 |  |  |                                 if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong))))
 | 
      
         | 166 |  |  |                                         return err;
 | 
      
         | 167 |  |  |                                 err = sys_shmat (first, (char *) ptr, second, &raddr);
 | 
      
         | 168 |  |  |                                 if (err)
 | 
      
         | 169 |  |  |                                         return err;
 | 
      
         | 170 |  |  |                                 put_fs_long (raddr, (ulong *) third);
 | 
      
         | 171 |  |  |                                 return 0;
 | 
      
         | 172 |  |  |                                 }
 | 
      
         | 173 |  |  |                         case 1: /* iBCS2 emulator entry point */
 | 
      
         | 174 |  |  |                                 if (get_fs() != get_ds())
 | 
      
         | 175 |  |  |                                         return -EINVAL;
 | 
      
         | 176 |  |  |                                 return sys_shmat (first, (char *) ptr, second, (ulong *) third);
 | 
      
         | 177 |  |  |                         }
 | 
      
         | 178 |  |  |                 case SHMDT:
 | 
      
         | 179 |  |  |                         return sys_shmdt ((char *)ptr);
 | 
      
         | 180 |  |  |                 case SHMGET:
 | 
      
         | 181 |  |  |                         return sys_shmget (first, second, third);
 | 
      
         | 182 |  |  |                 case SHMCTL:
 | 
      
         | 183 |  |  |                         return sys_shmctl (first, second, (struct shmid_ds *) ptr);
 | 
      
         | 184 |  |  |                 default:
 | 
      
         | 185 |  |  |                         return -EINVAL;
 | 
      
         | 186 |  |  |                 }
 | 
      
         | 187 |  |  |         return -EINVAL;
 | 
      
         | 188 |  |  | }
 | 
      
         | 189 |  |  |  
 | 
      
         | 190 |  |  | static inline unsigned long get_instr(struct pt_regs *regs)
 | 
      
         | 191 |  |  | {
 | 
      
         | 192 |  |  |     unsigned long *pcv;
 | 
      
         | 193 |  |  |  
 | 
      
         | 194 |  |  |     pcv = (unsigned long *)instruction_pointer(regs);
 | 
      
         | 195 |  |  |  
 | 
      
         | 196 |  |  |     return get_user (pcv);
 | 
      
         | 197 |  |  | }
 | 
      
         | 198 |  |  |  
 | 
      
         | 199 |  |  | /*
 | 
      
         | 200 |  |  |  * Old functions - we used to pass 5 parameters as r0, r1, r2, *r3, *(r3+4)
 | 
      
         | 201 |  |  |  * Now use r0 - r4.  Note that these will stay for a while until people have upgraded...
 | 
      
         | 202 |  |  |  * (so that we can still use old binaries).
 | 
      
         | 203 |  |  |  *
 | 
      
         | 204 |  |  |  * Eventually these functions will disappear.
 | 
      
         | 205 |  |  |  */
 | 
      
         | 206 |  |  | asmlinkage int
 | 
      
         | 207 |  |  | sys_old_arm_init_module (char *module_name, char *code, unsigned codesize,
 | 
      
         | 208 |  |  |         struct mod_routines *modr, struct symbol_table *syms)
 | 
      
         | 209 |  |  | {
 | 
      
         | 210 |  |  |     extern int sys_init_module (char *, char *, unsigned, struct mod_routines *,
 | 
      
         | 211 |  |  |                 struct symbol_table *);
 | 
      
         | 212 |  |  |     struct pt_regs *regs;
 | 
      
         | 213 |  |  |  
 | 
      
         | 214 |  |  |     __asm__ ("mov %0, r9": "=r" (regs));
 | 
      
         | 215 |  |  |  
 | 
      
         | 216 |  |  |     if (get_instr(regs) == 0xe1a0300d) {
 | 
      
         | 217 |  |  |         unsigned long *__p;
 | 
      
         | 218 |  |  |         __p = (unsigned long *)modr;
 | 
      
         | 219 |  |  |         if (!(current->flags & 0x40000000)) {
 | 
      
         | 220 |  |  |                 printk (KERN_NOTICE "%s (%d): Old init_module call\n", current->comm, current->pid);
 | 
      
         | 221 |  |  |                 current->flags |= 0x40000000;
 | 
      
         | 222 |  |  |         }
 | 
      
         | 223 |  |  |         return sys_init_module (module_name, code, codesize, (struct mod_routines *)__p[0],
 | 
      
         | 224 |  |  |                 (struct symbol_table *)__p[1]);
 | 
      
         | 225 |  |  |     }
 | 
      
         | 226 |  |  |     return sys_init_module (module_name, code, codesize, modr, syms);
 | 
      
         | 227 |  |  | }
 | 
      
         | 228 |  |  |  
 | 
      
         | 229 |  |  | asmlinkage int
 | 
      
         | 230 |  |  | sys_old_arm_llseek (unsigned int fd, unsigned long offset_high, unsigned long offset_low,
 | 
      
         | 231 |  |  |                 loff_t *result, unsigned int origin)
 | 
      
         | 232 |  |  | {
 | 
      
         | 233 |  |  |     extern int sys_llseek (unsigned int, unsigned long, unsigned long, loff_t *, unsigned int);
 | 
      
         | 234 |  |  |     struct pt_regs *regs;
 | 
      
         | 235 |  |  |  
 | 
      
         | 236 |  |  |     __asm__ ("mov %0, r9": "=r" (regs));
 | 
      
         | 237 |  |  |  
 | 
      
         | 238 |  |  |     if (get_instr(regs) == 0xe1a0300d) {
 | 
      
         | 239 |  |  |         unsigned long *__p;
 | 
      
         | 240 |  |  |         __p = (unsigned long *)result;
 | 
      
         | 241 |  |  |         if (!(current->flags & 0x20000000)) {
 | 
      
         | 242 |  |  |                 printk (KERN_NOTICE "%s (%d): Old llseek call\n", current->comm, current->pid);
 | 
      
         | 243 |  |  |                 current->flags |= 0x20000000;
 | 
      
         | 244 |  |  |         }
 | 
      
         | 245 |  |  |         return sys_llseek (fd, offset_high, offset_low, (loff_t *)__p[0], (unsigned int)__p[1]);
 | 
      
         | 246 |  |  |     }
 | 
      
         | 247 |  |  |     return sys_llseek (fd, offset_high, offset_low, result, origin);
 | 
      
         | 248 |  |  | }
 | 
      
         | 249 |  |  |  
 | 
      
         | 250 |  |  | asmlinkage int
 | 
      
         | 251 |  |  | sys_old_arm_mount (char *devname, char *dirname, char *type, unsigned long flags, void *data)
 | 
      
         | 252 |  |  | {
 | 
      
         | 253 |  |  |     extern int sys_mount (char *, char *, char *, unsigned long, void *);
 | 
      
         | 254 |  |  |     struct pt_regs *regs;
 | 
      
         | 255 |  |  |  
 | 
      
         | 256 |  |  |     __asm__ ("mov %0, r9": "=r" (regs));
 | 
      
         | 257 |  |  |  
 | 
      
         | 258 |  |  |     if (get_instr(regs) == 0xe1a0300d) {
 | 
      
         | 259 |  |  |         unsigned long *__p;
 | 
      
         | 260 |  |  |         __p = (unsigned long *)flags;
 | 
      
         | 261 |  |  |         if (!(current->flags & 0x10000000)) {
 | 
      
         | 262 |  |  |                 printk (KERN_NOTICE "%s (%d): Old mount call\n", current->comm, current->pid);
 | 
      
         | 263 |  |  |                 current->flags |= 0x10000000;
 | 
      
         | 264 |  |  |         }
 | 
      
         | 265 |  |  |         return sys_mount (devname, dirname, type, __p[0], (void *)__p[1]);
 | 
      
         | 266 |  |  |     }
 | 
      
         | 267 |  |  |     return sys_mount (devname, dirname, type, flags, data);
 | 
      
         | 268 |  |  | }
 |