Merge branch 'misc' into release
[linux-2.6/mini2440.git] / arch / x86 / kernel / sys_x86_64.c
blob6bc211accf087ed2be1a381395eae95093f4dbf4
1 #include <linux/errno.h>
2 #include <linux/sched.h>
3 #include <linux/syscalls.h>
4 #include <linux/mm.h>
5 #include <linux/fs.h>
6 #include <linux/smp.h>
7 #include <linux/sem.h>
8 #include <linux/msg.h>
9 #include <linux/shm.h>
10 #include <linux/stat.h>
11 #include <linux/mman.h>
12 #include <linux/file.h>
13 #include <linux/utsname.h>
14 #include <linux/personality.h>
15 #include <linux/random.h>
16 #include <linux/uaccess.h>
18 #include <asm/ia32.h>
19 #include <asm/syscalls.h>
21 asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
22 unsigned long prot, unsigned long flags,
23 unsigned long fd, unsigned long off)
25 long error;
26 struct file *file;
28 error = -EINVAL;
29 if (off & ~PAGE_MASK)
30 goto out;
32 error = -EBADF;
33 file = NULL;
34 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
35 if (!(flags & MAP_ANONYMOUS)) {
36 file = fget(fd);
37 if (!file)
38 goto out;
40 down_write(&current->mm->mmap_sem);
41 error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);
42 up_write(&current->mm->mmap_sem);
44 if (file)
45 fput(file);
46 out:
47 return error;
50 static void find_start_end(unsigned long flags, unsigned long *begin,
51 unsigned long *end)
53 if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT)) {
54 unsigned long new_begin;
55 /* This is usually used needed to map code in small
56 model, so it needs to be in the first 31bit. Limit
57 it to that. This means we need to move the
58 unmapped base down for this case. This can give
59 conflicts with the heap, but we assume that glibc
60 malloc knows how to fall back to mmap. Give it 1GB
61 of playground for now. -AK */
62 *begin = 0x40000000;
63 *end = 0x80000000;
64 if (current->flags & PF_RANDOMIZE) {
65 new_begin = randomize_range(*begin, *begin + 0x02000000, 0);
66 if (new_begin)
67 *begin = new_begin;
69 } else {
70 *begin = TASK_UNMAPPED_BASE;
71 *end = TASK_SIZE;
75 unsigned long
76 arch_get_unmapped_area(struct file *filp, unsigned long addr,
77 unsigned long len, unsigned long pgoff, unsigned long flags)
79 struct mm_struct *mm = current->mm;
80 struct vm_area_struct *vma;
81 unsigned long start_addr;
82 unsigned long begin, end;
84 if (flags & MAP_FIXED)
85 return addr;
87 find_start_end(flags, &begin, &end);
89 if (len > end)
90 return -ENOMEM;
92 if (addr) {
93 addr = PAGE_ALIGN(addr);
94 vma = find_vma(mm, addr);
95 if (end - len >= addr &&
96 (!vma || addr + len <= vma->vm_start))
97 return addr;
99 if (((flags & MAP_32BIT) || test_thread_flag(TIF_IA32))
100 && len <= mm->cached_hole_size) {
101 mm->cached_hole_size = 0;
102 mm->free_area_cache = begin;
104 addr = mm->free_area_cache;
105 if (addr < begin)
106 addr = begin;
107 start_addr = addr;
109 full_search:
110 for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
111 /* At this point: (!vma || addr < vma->vm_end). */
112 if (end - len < addr) {
114 * Start a new search - just in case we missed
115 * some holes.
117 if (start_addr != begin) {
118 start_addr = addr = begin;
119 mm->cached_hole_size = 0;
120 goto full_search;
122 return -ENOMEM;
124 if (!vma || addr + len <= vma->vm_start) {
126 * Remember the place where we stopped the search:
128 mm->free_area_cache = addr + len;
129 return addr;
131 if (addr + mm->cached_hole_size < vma->vm_start)
132 mm->cached_hole_size = vma->vm_start - addr;
134 addr = vma->vm_end;
139 unsigned long
140 arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
141 const unsigned long len, const unsigned long pgoff,
142 const unsigned long flags)
144 struct vm_area_struct *vma;
145 struct mm_struct *mm = current->mm;
146 unsigned long addr = addr0;
148 /* requested length too big for entire address space */
149 if (len > TASK_SIZE)
150 return -ENOMEM;
152 if (flags & MAP_FIXED)
153 return addr;
155 /* for MAP_32BIT mappings we force the legact mmap base */
156 if (!test_thread_flag(TIF_IA32) && (flags & MAP_32BIT))
157 goto bottomup;
159 /* requesting a specific address */
160 if (addr) {
161 addr = PAGE_ALIGN(addr);
162 vma = find_vma(mm, addr);
163 if (TASK_SIZE - len >= addr &&
164 (!vma || addr + len <= vma->vm_start))
165 return addr;
168 /* check if free_area_cache is useful for us */
169 if (len <= mm->cached_hole_size) {
170 mm->cached_hole_size = 0;
171 mm->free_area_cache = mm->mmap_base;
174 /* either no address requested or can't fit in requested address hole */
175 addr = mm->free_area_cache;
177 /* make sure it can fit in the remaining address space */
178 if (addr > len) {
179 vma = find_vma(mm, addr-len);
180 if (!vma || addr <= vma->vm_start)
181 /* remember the address as a hint for next time */
182 return mm->free_area_cache = addr-len;
185 if (mm->mmap_base < len)
186 goto bottomup;
188 addr = mm->mmap_base-len;
190 do {
192 * Lookup failure means no vma is above this address,
193 * else if new region fits below vma->vm_start,
194 * return with success:
196 vma = find_vma(mm, addr);
197 if (!vma || addr+len <= vma->vm_start)
198 /* remember the address as a hint for next time */
199 return mm->free_area_cache = addr;
201 /* remember the largest hole we saw so far */
202 if (addr + mm->cached_hole_size < vma->vm_start)
203 mm->cached_hole_size = vma->vm_start - addr;
205 /* try just below the current vma->vm_start */
206 addr = vma->vm_start-len;
207 } while (len < vma->vm_start);
209 bottomup:
211 * A failed mmap() very likely causes application failure,
212 * so fall back to the bottom-up function here. This scenario
213 * can happen with large stack limits and large mmap()
214 * allocations.
216 mm->cached_hole_size = ~0UL;
217 mm->free_area_cache = TASK_UNMAPPED_BASE;
218 addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
220 * Restore the topdown base:
222 mm->free_area_cache = mm->mmap_base;
223 mm->cached_hole_size = ~0UL;
225 return addr;
229 asmlinkage long sys_uname(struct new_utsname __user *name)
231 int err;
232 down_read(&uts_sem);
233 err = copy_to_user(name, utsname(), sizeof(*name));
234 up_read(&uts_sem);
235 if (personality(current->personality) == PER_LINUX32)
236 err |= copy_to_user(&name->machine, "i686", 5);
237 return err ? -EFAULT : 0;