blob: 2fcaa2051aa3855791899aaeceada807bb490ee3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This file contains various system calls that have different calling
3 * conventions on different platforms.
4 *
5 * Copyright (C) 1999-2000, 2002-2003, 2005 Hewlett-Packard Co
6 * David Mosberger-Tang <davidm@hpl.hp.com>
7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/errno.h>
9#include <linux/fs.h>
10#include <linux/mm.h>
11#include <linux/mman.h>
12#include <linux/sched.h>
13#include <linux/shm.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/syscalls.h>
18#include <linux/highuid.h>
19#include <linux/hugetlb.h>
20
21#include <asm/shmparam.h>
22#include <asm/uaccess.h>
23
24unsigned long
25arch_get_unmapped_area (struct file *filp, unsigned long addr, unsigned long len,
26 unsigned long pgoff, unsigned long flags)
27{
28 long map_shared = (flags & MAP_SHARED);
29 unsigned long start_addr, align_mask = PAGE_SIZE - 1;
30 struct mm_struct *mm = current->mm;
31 struct vm_area_struct *vma;
32
33 if (len > RGN_MAP_LIMIT)
34 return -ENOMEM;
35
Benjamin Herrenschmidtafa37392007-05-06 14:50:09 -070036 /* handle fixed mapping: prevent overlap with huge pages */
37 if (flags & MAP_FIXED) {
38 if (is_hugepage_only_range(mm, addr, len))
39 return -EINVAL;
40 return addr;
41 }
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#ifdef CONFIG_HUGETLB_PAGE
Peter Chubb0a41e252005-08-16 19:54:00 -070044 if (REGION_NUMBER(addr) == RGN_HPAGE)
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 addr = 0;
46#endif
47 if (!addr)
48 addr = mm->free_area_cache;
49
50 if (map_shared && (TASK_SIZE > 0xfffffffful))
51 /*
52 * For 64-bit tasks, align shared segments to 1MB to avoid potential
53 * performance penalty due to virtual aliasing (see ASDM). For 32-bit
54 * tasks, we prefer to avoid exhausting the address space too quickly by
55 * limiting alignment to a single page.
56 */
57 align_mask = SHMLBA - 1;
58
59 full_search:
60 start_addr = addr = (addr + align_mask) & ~align_mask;
61
62 for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
63 /* At this point: (!vma || addr < vma->vm_end). */
64 if (TASK_SIZE - len < addr || RGN_MAP_LIMIT - len < REGION_OFFSET(addr)) {
65 if (start_addr != TASK_UNMAPPED_BASE) {
66 /* Start a new search --- just in case we missed some holes. */
67 addr = TASK_UNMAPPED_BASE;
68 goto full_search;
69 }
70 return -ENOMEM;
71 }
72 if (!vma || addr + len <= vma->vm_start) {
73 /* Remember the address where we stopped this search: */
74 mm->free_area_cache = addr + len;
75 return addr;
76 }
77 addr = (vma->vm_end + align_mask) & ~align_mask;
78 }
79}
80
81asmlinkage long
82ia64_getpriority (int which, int who)
83{
84 long prio;
85
86 prio = sys_getpriority(which, who);
87 if (prio >= 0) {
88 force_successful_syscall_return();
89 prio = 20 - prio;
90 }
91 return prio;
92}
93
94/* XXX obsolete, but leave it here until the old libc is gone... */
95asmlinkage unsigned long
96sys_getpagesize (void)
97{
98 return PAGE_SIZE;
99}
100
101asmlinkage unsigned long
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102ia64_brk (unsigned long brk)
103{
104 unsigned long rlim, retval, newbrk, oldbrk;
105 struct mm_struct *mm = current->mm;
106
107 /*
108 * Most of this replicates the code in sys_brk() except for an additional safety
109 * check and the clearing of r8. However, we can't call sys_brk() because we need
110 * to acquire the mmap_sem before we can do the test...
111 */
112 down_write(&mm->mmap_sem);
113
114 if (brk < mm->end_code)
115 goto out;
116 newbrk = PAGE_ALIGN(brk);
117 oldbrk = PAGE_ALIGN(mm->brk);
118 if (oldbrk == newbrk)
119 goto set_brk;
120
121 /* Always allow shrinking brk. */
122 if (brk <= mm->brk) {
123 if (!do_munmap(mm, newbrk, oldbrk-newbrk))
124 goto set_brk;
125 goto out;
126 }
127
128 /* Check against unimplemented/unmapped addresses: */
129 if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT)
130 goto out;
131
132 /* Check against rlimit.. */
133 rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
134 if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
135 goto out;
136
137 /* Check against existing mmap mappings. */
138 if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
139 goto out;
140
141 /* Ok, looks good - let it rip. */
142 if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
143 goto out;
144set_brk:
145 mm->brk = brk;
146out:
147 retval = mm->brk;
148 up_write(&mm->mmap_sem);
149 force_successful_syscall_return();
150 return retval;
151}
152
153/*
154 * On IA-64, we return the two file descriptors in ret0 and ret1 (r8
155 * and r9) as this is faster than doing a copy_to_user().
156 */
157asmlinkage long
158sys_pipe (void)
159{
Al Viro64505782006-01-12 01:06:06 -0800160 struct pt_regs *regs = task_pt_regs(current);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 int fd[2];
162 int retval;
163
164 retval = do_pipe(fd);
165 if (retval)
166 goto out;
167 retval = fd[0];
168 regs->r9 = fd[1];
169 out:
170 return retval;
171}
172
Kirill Korotaev3a459752006-09-07 14:17:04 +0400173int ia64_mmap_check(unsigned long addr, unsigned long len,
174 unsigned long flags)
175{
176 unsigned long roff;
177
178 /*
179 * Don't permit mappings into unmapped space, the virtual page table
180 * of a region, or across a region boundary. Note: RGN_MAP_LIMIT is
181 * equal to 2^n-PAGE_SIZE (for some integer n <= 61) and len > 0.
182 */
183 roff = REGION_OFFSET(addr);
184 if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len)))
185 return -EINVAL;
186 return 0;
187}
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189static inline unsigned long
190do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
191{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 struct file *file = NULL;
193
194 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
195 if (!(flags & MAP_ANONYMOUS)) {
196 file = fget(fd);
197 if (!file)
198 return -EBADF;
199
200 if (!file->f_op || !file->f_op->mmap) {
201 addr = -ENODEV;
202 goto out;
203 }
204 }
205
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 /* Careful about overflows.. */
207 len = PAGE_ALIGN(len);
208 if (!len || len > TASK_SIZE) {
209 addr = -EINVAL;
210 goto out;
211 }
212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 down_write(&current->mm->mmap_sem);
214 addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
215 up_write(&current->mm->mmap_sem);
216
217out: if (file)
218 fput(file);
219 return addr;
220}
221
222/*
223 * mmap2() is like mmap() except that the offset is expressed in units
224 * of PAGE_SIZE (instead of bytes). This allows to mmap2() (pieces
225 * of) files that are larger than the address space of the CPU.
226 */
227asmlinkage unsigned long
228sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff)
229{
230 addr = do_mmap2(addr, len, prot, flags, fd, pgoff);
231 if (!IS_ERR((void *) addr))
232 force_successful_syscall_return();
233 return addr;
234}
235
236asmlinkage unsigned long
237sys_mmap (unsigned long addr, unsigned long len, int prot, int flags, int fd, long off)
238{
239 if (offset_in_page(off) != 0)
240 return -EINVAL;
241
242 addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
243 if (!IS_ERR((void *) addr))
244 force_successful_syscall_return();
245 return addr;
246}
247
248asmlinkage unsigned long
249ia64_mremap (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags,
250 unsigned long new_addr)
251{
252 extern unsigned long do_mremap (unsigned long addr,
253 unsigned long old_len,
254 unsigned long new_len,
255 unsigned long flags,
256 unsigned long new_addr);
257
258 down_write(&current->mm->mmap_sem);
259 {
260 addr = do_mremap(addr, old_len, new_len, flags, new_addr);
261 }
262 up_write(&current->mm->mmap_sem);
263
264 if (IS_ERR((void *) addr))
265 return addr;
266
267 force_successful_syscall_return();
268 return addr;
269}
270
271#ifndef CONFIG_PCI
272
273asmlinkage long
274sys_pciconfig_read (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
275 void *buf)
276{
277 return -ENOSYS;
278}
279
280asmlinkage long
281sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len,
282 void *buf)
283{
284 return -ENOSYS;
285}
286
287#endif /* CONFIG_PCI */