File: /usr/src/linux/arch/ia64/kernel/sys_ia64.c
1 /*
2 * This file contains various system calls that have different calling
3 * conventions on different platforms.
4 *
5 * Copyright (C) 1999-2000 Hewlett-Packard Co
6 * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
7 */
8 #include <linux/config.h>
9 #include <linux/errno.h>
10 #include <linux/fs.h>
11 #include <linux/mm.h>
12 #include <linux/mman.h>
13 #include <linux/sched.h>
14 #include <linux/file.h> /* doh, must come after sched.h... */
15 #include <linux/smp.h>
16 #include <linux/smp_lock.h>
17 #include <linux/highuid.h>
18
19 #include <asm/shmparam.h>
20 #include <asm/uaccess.h>
21
22 #define COLOR_ALIGN(addr) (((addr) + SHMLBA - 1) & ~(SHMLBA - 1))
23
24 unsigned long
25 arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len,
26 unsigned long pgoff, unsigned long flags)
27 {
28 struct vm_area_struct * vmm;
29 long map_shared = (flags & MAP_SHARED);
30
31 if (len > RGN_MAP_LIMIT)
32 return -ENOMEM;
33 if (!addr)
34 addr = TASK_UNMAPPED_BASE;
35
36 if (map_shared)
37 addr = COLOR_ALIGN(addr);
38 else
39 addr = PAGE_ALIGN(addr);
40
41 for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
42 /* At this point: (!vmm || addr < vmm->vm_end). */
43 if (TASK_SIZE - len < addr)
44 return -ENOMEM;
45 if (rgn_offset(addr) + len > RGN_MAP_LIMIT) /* no risk of overflow here... */
46 return -ENOMEM;
47 if (!vmm || addr + len <= vmm->vm_start)
48 return addr;
49 addr = vmm->vm_end;
50 if (map_shared)
51 addr = COLOR_ALIGN(addr);
52 }
53 }
54
55 asmlinkage long
56 ia64_getpriority (int which, int who, long arg2, long arg3, long arg4, long arg5, long arg6,
57 long arg7, long stack)
58 {
59 struct pt_regs *regs = (struct pt_regs *) &stack;
60 extern long sys_getpriority (int, int);
61 long prio;
62
63 prio = sys_getpriority(which, who);
64 if (prio >= 0) {
65 regs->r8 = 0; /* ensure negative priority is not mistaken as error code */
66 prio = 20 - prio;
67 }
68 return prio;
69 }
70
71 /* XXX obsolete, but leave it here until the old libc is gone... */
72 asmlinkage unsigned long
73 sys_getpagesize (void)
74 {
75 return PAGE_SIZE;
76 }
77
78 asmlinkage unsigned long
79 ia64_shmat (int shmid, void *shmaddr, int shmflg, long arg3, long arg4, long arg5, long arg6,
80 long arg7, long stack)
81 {
82 extern int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr);
83 struct pt_regs *regs = (struct pt_regs *) &stack;
84 unsigned long raddr;
85 int retval;
86
87 retval = sys_shmat(shmid, shmaddr, shmflg, &raddr);
88 if (retval < 0)
89 return retval;
90
91 regs->r8 = 0; /* ensure negative addresses are not mistaken as an error code */
92 return raddr;
93 }
94
95 asmlinkage unsigned long
96 ia64_brk (unsigned long brk, long arg1, long arg2, long arg3,
97 long arg4, long arg5, long arg6, long arg7, long stack)
98 {
99 extern int vm_enough_memory (long pages);
100 struct pt_regs *regs = (struct pt_regs *) &stack;
101 unsigned long rlim, retval, newbrk, oldbrk;
102 struct mm_struct *mm = current->mm;
103
104 /*
105 * Most of this replicates the code in sys_brk() except for an additional safety
106 * check and the clearing of r8. However, we can't call sys_brk() because we need
107 * to acquire the mmap_sem before we can do the test...
108 */
109 down_write(&mm->mmap_sem);
110
111 if (brk < mm->end_code)
112 goto out;
113 newbrk = PAGE_ALIGN(brk);
114 oldbrk = PAGE_ALIGN(mm->brk);
115 if (oldbrk == newbrk)
116 goto set_brk;
117
118 /* Always allow shrinking brk. */
119 if (brk <= mm->brk) {
120 if (!do_munmap(mm, newbrk, oldbrk-newbrk))
121 goto set_brk;
122 goto out;
123 }
124
125 /* Check against unimplemented/unmapped addresses: */
126 if ((newbrk - oldbrk) > RGN_MAP_LIMIT || rgn_offset(newbrk) > RGN_MAP_LIMIT)
127 goto out;
128
129 /* Check against rlimit.. */
130 rlim = current->rlim[RLIMIT_DATA].rlim_cur;
131 if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
132 goto out;
133
134 /* Check against existing mmap mappings. */
135 if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
136 goto out;
137
138 /* Check if we have enough memory.. */
139 if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT))
140 goto out;
141
142 /* Ok, looks good - let it rip. */
143 if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
144 goto out;
145 set_brk:
146 mm->brk = brk;
147 out:
148 retval = mm->brk;
149 up_write(&mm->mmap_sem);
150 regs->r8 = 0; /* ensure large retval isn't mistaken as error code */
151 return retval;
152 }
153
154 /*
155 * On IA-64, we return the two file descriptors in ret0 and ret1 (r8
156 * and r9) as this is faster than doing a copy_to_user().
157 */
158 asmlinkage long
159 sys_pipe (long arg0, long arg1, long arg2, long arg3,
160 long arg4, long arg5, long arg6, long arg7, long stack)
161 {
162 struct pt_regs *regs = (struct pt_regs *) &stack;
163 int fd[2];
164 int retval;
165
166 retval = do_pipe(fd);
167 if (retval)
168 goto out;
169 retval = fd[0];
170 regs->r9 = fd[1];
171 out:
172 return retval;
173 }
174
175 static inline unsigned long
176 do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
177 {
178 unsigned long roff;
179 struct file *file = 0;
180
181 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
182 if (!(flags & MAP_ANONYMOUS)) {
183 file = fget(fd);
184 if (!file)
185 return -EBADF;
186
187 if (!file->f_op || !file->f_op->mmap)
188 return -ENODEV;
189 }
190
191 /*
192 * A zero mmap always succeeds in Linux, independent of whether or not the
193 * remaining arguments are valid.
194 */
195 len = PAGE_ALIGN(len);
196 if (len == 0)
197 return addr;
198
199 /* don't permit mappings into unmapped space or the virtual page table of a region: */
200 roff = rgn_offset(addr);
201 if ((len | roff | (roff + len)) >= RGN_MAP_LIMIT)
202 return -EINVAL;
203
204 /* don't permit mappings that would cross a region boundary: */
205 if (rgn_index(addr) != rgn_index(addr + len))
206 return -EINVAL;
207
208 down_write(¤t->mm->mmap_sem);
209 addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
210 up_write(¤t->mm->mmap_sem);
211
212 if (file)
213 fput(file);
214 return addr;
215 }
216
217 /*
218 * mmap2() is like mmap() except that the offset is expressed in units
219 * of PAGE_SIZE (instead of bytes). This allows to mmap2() (pieces
220 * of) files that are larger than the address space of the CPU.
221 */
222 asmlinkage unsigned long
223 sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff,
224 long arg6, long arg7, long stack)
225 {
226 struct pt_regs *regs = (struct pt_regs *) &stack;
227
228 addr = do_mmap2(addr, len, prot, flags, fd, pgoff);
229 if (!IS_ERR((void *) addr))
230 regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */
231 return addr;
232 }
233
234 asmlinkage unsigned long
235 sys_mmap (unsigned long addr, unsigned long len, int prot, int flags,
236 int fd, long off, long arg6, long arg7, long stack)
237 {
238 struct pt_regs *regs = (struct pt_regs *) &stack;
239
240 if ((off & ~PAGE_MASK) != 0)
241 return -EINVAL;
242
243 addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
244 if (!IS_ERR((void *) addr))
245 regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */
246 return addr;
247 }
248
249 asmlinkage long
250 sys_vm86 (long arg0, long arg1, long arg2, long arg3)
251 {
252 printk(KERN_ERR "sys_vm86(%lx, %lx, %lx, %lx)!\n", arg0, arg1, arg2, arg3);
253 return -ENOSYS;
254 }
255
256 asmlinkage unsigned long
257 ia64_create_module (const char *name_user, size_t size, long arg2, long arg3,
258 long arg4, long arg5, long arg6, long arg7, long stack)
259 {
260 extern unsigned long sys_create_module (const char *, size_t);
261 struct pt_regs *regs = (struct pt_regs *) &stack;
262 unsigned long addr;
263
264 addr = sys_create_module (name_user, size);
265 if (!IS_ERR((void *) addr))
266 regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */
267 return addr;
268 }
269
270 #if 1
271 /*
272 * This is here for a while to keep compatibillity with the old stat()
273 * call - it will be removed later once everybody migrates to the new
274 * kernel stat structure that matches the glibc one - Jes
275 */
276 static __inline__ int
277 do_revalidate (struct dentry *dentry)
278 {
279 struct inode * inode = dentry->d_inode;
280 if (inode->i_op && inode->i_op->revalidate)
281 return inode->i_op->revalidate(dentry);
282 return 0;
283 }
284
285 static int
286 cp_ia64_old_stat (struct inode *inode, struct ia64_oldstat *statbuf)
287 {
288 struct ia64_oldstat tmp;
289 unsigned int blocks, indirect;
290
291 memset(&tmp, 0, sizeof(tmp));
292 tmp.st_dev = kdev_t_to_nr(inode->i_dev);
293 tmp.st_ino = inode->i_ino;
294 tmp.st_mode = inode->i_mode;
295 tmp.st_nlink = inode->i_nlink;
296 SET_STAT_UID(tmp, inode->i_uid);
297 SET_STAT_GID(tmp, inode->i_gid);
298 tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
299 tmp.st_size = inode->i_size;
300 tmp.st_atime = inode->i_atime;
301 tmp.st_mtime = inode->i_mtime;
302 tmp.st_ctime = inode->i_ctime;
303 /*
304 * st_blocks and st_blksize are approximated with a simple algorithm if
305 * they aren't supported directly by the filesystem. The minix and msdos
306 * filesystems don't keep track of blocks, so they would either have to
307 * be counted explicitly (by delving into the file itself), or by using
308 * this simple algorithm to get a reasonable (although not 100% accurate)
309 * value.
310 */
311
312 /*
313 * Use minix fs values for the number of direct and indirect blocks. The
314 * count is now exact for the minix fs except that it counts zero blocks.
315 * Everything is in units of BLOCK_SIZE until the assignment to
316 * tmp.st_blksize.
317 */
318 #define D_B 7
319 #define I_B (BLOCK_SIZE / sizeof(unsigned short))
320
321 if (!inode->i_blksize) {
322 blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
323 if (blocks > D_B) {
324 indirect = (blocks - D_B + I_B - 1) / I_B;
325 blocks += indirect;
326 if (indirect > 1) {
327 indirect = (indirect - 1 + I_B - 1) / I_B;
328 blocks += indirect;
329 if (indirect > 1)
330 blocks++;
331 }
332 }
333 tmp.st_blocks = (BLOCK_SIZE / 512) * blocks;
334 tmp.st_blksize = BLOCK_SIZE;
335 } else {
336 tmp.st_blocks = inode->i_blocks;
337 tmp.st_blksize = inode->i_blksize;
338 }
339 return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
340 }
341
342 asmlinkage long
343 ia64_oldstat (char *filename, struct ia64_oldstat *statbuf)
344 {
345 struct nameidata nd;
346 int error;
347
348 error = user_path_walk(filename, &nd);
349 if (!error) {
350 error = do_revalidate(nd.dentry);
351 if (!error)
352 error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf);
353 path_release(&nd);
354 }
355 return error;
356 }
357
358
359 asmlinkage long
360 ia64_oldlstat (char *filename, struct ia64_oldstat *statbuf) {
361 struct nameidata nd;
362 int error;
363
364 error = user_path_walk_link(filename, &nd);
365 if (!error) {
366 error = do_revalidate(nd.dentry);
367 if (!error)
368 error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf);
369 path_release(&nd);
370 }
371 return error;
372 }
373
374 asmlinkage long
375 ia64_oldfstat (unsigned int fd, struct ia64_oldstat *statbuf)
376 {
377 struct file * f;
378 int err = -EBADF;
379
380 f = fget(fd);
381 if (f) {
382 struct dentry * dentry = f->f_dentry;
383
384 err = do_revalidate(dentry);
385 if (!err)
386 err = cp_ia64_old_stat(dentry->d_inode, statbuf);
387 fput(f);
388 }
389 return err;
390 }
391
392 #endif
393
394 #ifndef CONFIG_PCI
395
396 asmlinkage long
397 sys_pciconfig_read (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
398 void *buf)
399 {
400 return -ENOSYS;
401 }
402
403 asmlinkage long
404 sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
405 void *buf)
406 {
407 return -ENOSYS;
408 }
409
410 #endif /* CONFIG_PCI */
411