diff options
author | Tony Luck <tony.luck@intel.com> | 2010-02-08 13:42:17 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2010-02-08 13:42:17 -0500 |
commit | 32974ad4907cdde6c9de612cd1b2ee0568fb9409 (patch) | |
tree | 46d883f7f4fb3f4a5cee8ec9eb2c6b4939d7ae10 /arch/ia64/ia32/sys_ia32.c | |
parent | 6339204ecc2aa2067a99595522de0403f0854bb8 (diff) |
[IA64] Remove COMPAT_IA32 support
This has been broken since May 2008 when Al Viro killed altroot support.
Since nobody has complained, it would appear that there are no users of
this code (A plausible theory since the main OSVs that support ia64 prefer
to use the IA32-EL software emulation).
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch/ia64/ia32/sys_ia32.c')
-rw-r--r-- | arch/ia64/ia32/sys_ia32.c | 2765 |
1 files changed, 0 insertions, 2765 deletions
diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c deleted file mode 100644 index 045b746b9808..000000000000 --- a/arch/ia64/ia32/sys_ia32.c +++ /dev/null | |||
@@ -1,2765 +0,0 @@ | |||
1 | /* | ||
2 | * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Derived from sys_sparc32.c. | ||
3 | * | ||
4 | * Copyright (C) 2000 VA Linux Co | ||
5 | * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> | ||
6 | * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> | ||
7 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | ||
8 | * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) | ||
9 | * Copyright (C) 2000-2003, 2005 Hewlett-Packard Co | ||
10 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
11 | * Copyright (C) 2004 Gordon Jin <gordon.jin@intel.com> | ||
12 | * | ||
13 | * These routines maintain argument size conversion between 32bit and 64bit | ||
14 | * environment. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/syscalls.h> | ||
19 | #include <linux/sysctl.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/file.h> | ||
23 | #include <linux/signal.h> | ||
24 | #include <linux/resource.h> | ||
25 | #include <linux/times.h> | ||
26 | #include <linux/utsname.h> | ||
27 | #include <linux/smp.h> | ||
28 | #include <linux/smp_lock.h> | ||
29 | #include <linux/sem.h> | ||
30 | #include <linux/msg.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/shm.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/uio.h> | ||
35 | #include <linux/socket.h> | ||
36 | #include <linux/quota.h> | ||
37 | #include <linux/poll.h> | ||
38 | #include <linux/eventpoll.h> | ||
39 | #include <linux/personality.h> | ||
40 | #include <linux/ptrace.h> | ||
41 | #include <linux/regset.h> | ||
42 | #include <linux/stat.h> | ||
43 | #include <linux/ipc.h> | ||
44 | #include <linux/capability.h> | ||
45 | #include <linux/compat.h> | ||
46 | #include <linux/vfs.h> | ||
47 | #include <linux/mman.h> | ||
48 | #include <linux/mutex.h> | ||
49 | |||
50 | #include <asm/intrinsics.h> | ||
51 | #include <asm/types.h> | ||
52 | #include <asm/uaccess.h> | ||
53 | #include <asm/unistd.h> | ||
54 | |||
55 | #include "ia32priv.h" | ||
56 | |||
57 | #include <net/scm.h> | ||
58 | #include <net/sock.h> | ||
59 | |||
60 | #define DEBUG 0 | ||
61 | |||
62 | #if DEBUG | ||
63 | # define DBG(fmt...) printk(KERN_DEBUG fmt) | ||
64 | #else | ||
65 | # define DBG(fmt...) | ||
66 | #endif | ||
67 | |||
68 | #define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) | ||
69 | |||
70 | #define OFFSET4K(a) ((a) & 0xfff) | ||
71 | #define PAGE_START(addr) ((addr) & PAGE_MASK) | ||
72 | #define MINSIGSTKSZ_IA32 2048 | ||
73 | |||
74 | #define high2lowuid(uid) ((uid) > 65535 ? 65534 : (uid)) | ||
75 | #define high2lowgid(gid) ((gid) > 65535 ? 65534 : (gid)) | ||
76 | |||
77 | /* | ||
78 | * Anything that modifies or inspects ia32 user virtual memory must hold this semaphore | ||
79 | * while doing so. | ||
80 | */ | ||
81 | /* XXX make per-mm: */ | ||
82 | static DEFINE_MUTEX(ia32_mmap_mutex); | ||
83 | |||
84 | asmlinkage long | ||
85 | sys32_execve (char __user *name, compat_uptr_t __user *argv, compat_uptr_t __user *envp, | ||
86 | struct pt_regs *regs) | ||
87 | { | ||
88 | long error; | ||
89 | char *filename; | ||
90 | unsigned long old_map_base, old_task_size, tssd; | ||
91 | |||
92 | filename = getname(name); | ||
93 | error = PTR_ERR(filename); | ||
94 | if (IS_ERR(filename)) | ||
95 | return error; | ||
96 | |||
97 | old_map_base = current->thread.map_base; | ||
98 | old_task_size = current->thread.task_size; | ||
99 | tssd = ia64_get_kr(IA64_KR_TSSD); | ||
100 | |||
101 | /* we may be exec'ing a 64-bit process: reset map base, task-size, and io-base: */ | ||
102 | current->thread.map_base = DEFAULT_MAP_BASE; | ||
103 | current->thread.task_size = DEFAULT_TASK_SIZE; | ||
104 | ia64_set_kr(IA64_KR_IO_BASE, current->thread.old_iob); | ||
105 | ia64_set_kr(IA64_KR_TSSD, current->thread.old_k1); | ||
106 | |||
107 | error = compat_do_execve(filename, argv, envp, regs); | ||
108 | putname(filename); | ||
109 | |||
110 | if (error < 0) { | ||
111 | /* oops, execve failed, switch back to old values... */ | ||
112 | ia64_set_kr(IA64_KR_IO_BASE, IA32_IOBASE); | ||
113 | ia64_set_kr(IA64_KR_TSSD, tssd); | ||
114 | current->thread.map_base = old_map_base; | ||
115 | current->thread.task_size = old_task_size; | ||
116 | } | ||
117 | |||
118 | return error; | ||
119 | } | ||
120 | |||
121 | |||
122 | #if PAGE_SHIFT > IA32_PAGE_SHIFT | ||
123 | |||
124 | |||
125 | static int | ||
126 | get_page_prot (struct vm_area_struct *vma, unsigned long addr) | ||
127 | { | ||
128 | int prot = 0; | ||
129 | |||
130 | if (!vma || vma->vm_start > addr) | ||
131 | return 0; | ||
132 | |||
133 | if (vma->vm_flags & VM_READ) | ||
134 | prot |= PROT_READ; | ||
135 | if (vma->vm_flags & VM_WRITE) | ||
136 | prot |= PROT_WRITE; | ||
137 | if (vma->vm_flags & VM_EXEC) | ||
138 | prot |= PROT_EXEC; | ||
139 | return prot; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Map a subpage by creating an anonymous page that contains the union of the old page and | ||
144 | * the subpage. | ||
145 | */ | ||
146 | static unsigned long | ||
147 | mmap_subpage (struct file *file, unsigned long start, unsigned long end, int prot, int flags, | ||
148 | loff_t off) | ||
149 | { | ||
150 | void *page = NULL; | ||
151 | struct inode *inode; | ||
152 | unsigned long ret = 0; | ||
153 | struct vm_area_struct *vma = find_vma(current->mm, start); | ||
154 | int old_prot = get_page_prot(vma, start); | ||
155 | |||
156 | DBG("mmap_subpage(file=%p,start=0x%lx,end=0x%lx,prot=%x,flags=%x,off=0x%llx)\n", | ||
157 | file, start, end, prot, flags, off); | ||
158 | |||
159 | |||
160 | /* Optimize the case where the old mmap and the new mmap are both anonymous */ | ||
161 | if ((old_prot & PROT_WRITE) && (flags & MAP_ANONYMOUS) && !vma->vm_file) { | ||
162 | if (clear_user((void __user *) start, end - start)) { | ||
163 | ret = -EFAULT; | ||
164 | goto out; | ||
165 | } | ||
166 | goto skip_mmap; | ||
167 | } | ||
168 | |||
169 | page = (void *) get_zeroed_page(GFP_KERNEL); | ||
170 | if (!page) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | if (old_prot) | ||
174 | copy_from_user(page, (void __user *) PAGE_START(start), PAGE_SIZE); | ||
175 | |||
176 | down_write(¤t->mm->mmap_sem); | ||
177 | { | ||
178 | ret = do_mmap(NULL, PAGE_START(start), PAGE_SIZE, prot | PROT_WRITE, | ||
179 | flags | MAP_FIXED | MAP_ANONYMOUS, 0); | ||
180 | } | ||
181 | up_write(¤t->mm->mmap_sem); | ||
182 | |||
183 | if (IS_ERR((void *) ret)) | ||
184 | goto out; | ||
185 | |||
186 | if (old_prot) { | ||
187 | /* copy back the old page contents. */ | ||
188 | if (offset_in_page(start)) | ||
189 | copy_to_user((void __user *) PAGE_START(start), page, | ||
190 | offset_in_page(start)); | ||
191 | if (offset_in_page(end)) | ||
192 | copy_to_user((void __user *) end, page + offset_in_page(end), | ||
193 | PAGE_SIZE - offset_in_page(end)); | ||
194 | } | ||
195 | |||
196 | if (!(flags & MAP_ANONYMOUS)) { | ||
197 | /* read the file contents */ | ||
198 | inode = file->f_path.dentry->d_inode; | ||
199 | if (!inode->i_fop || !file->f_op->read | ||
200 | || ((*file->f_op->read)(file, (char __user *) start, end - start, &off) < 0)) | ||
201 | { | ||
202 | ret = -EINVAL; | ||
203 | goto out; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | skip_mmap: | ||
208 | if (!(prot & PROT_WRITE)) | ||
209 | ret = sys_mprotect(PAGE_START(start), PAGE_SIZE, prot | old_prot); | ||
210 | out: | ||
211 | if (page) | ||
212 | free_page((unsigned long) page); | ||
213 | return ret; | ||
214 | } | ||
215 | |||
216 | /* SLAB cache for ia64_partial_page structures */ | ||
217 | struct kmem_cache *ia64_partial_page_cachep; | ||
218 | |||
219 | /* | ||
220 | * init ia64_partial_page_list. | ||
221 | * return 0 means kmalloc fail. | ||
222 | */ | ||
223 | struct ia64_partial_page_list* | ||
224 | ia32_init_pp_list(void) | ||
225 | { | ||
226 | struct ia64_partial_page_list *p; | ||
227 | |||
228 | if ((p = kmalloc(sizeof(*p), GFP_KERNEL)) == NULL) | ||
229 | return p; | ||
230 | p->pp_head = NULL; | ||
231 | p->ppl_rb = RB_ROOT; | ||
232 | p->pp_hint = NULL; | ||
233 | atomic_set(&p->pp_count, 1); | ||
234 | return p; | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Search for the partial page with @start in partial page list @ppl. | ||
239 | * If finds the partial page, return the found partial page. | ||
240 | * Else, return 0 and provide @pprev, @rb_link, @rb_parent to | ||
241 | * be used by later __ia32_insert_pp(). | ||
242 | */ | ||
243 | static struct ia64_partial_page * | ||
244 | __ia32_find_pp(struct ia64_partial_page_list *ppl, unsigned int start, | ||
245 | struct ia64_partial_page **pprev, struct rb_node ***rb_link, | ||
246 | struct rb_node **rb_parent) | ||
247 | { | ||
248 | struct ia64_partial_page *pp; | ||
249 | struct rb_node **__rb_link, *__rb_parent, *rb_prev; | ||
250 | |||
251 | pp = ppl->pp_hint; | ||
252 | if (pp && pp->base == start) | ||
253 | return pp; | ||
254 | |||
255 | __rb_link = &ppl->ppl_rb.rb_node; | ||
256 | rb_prev = __rb_parent = NULL; | ||
257 | |||
258 | while (*__rb_link) { | ||
259 | __rb_parent = *__rb_link; | ||
260 | pp = rb_entry(__rb_parent, struct ia64_partial_page, pp_rb); | ||
261 | |||
262 | if (pp->base == start) { | ||
263 | ppl->pp_hint = pp; | ||
264 | return pp; | ||
265 | } else if (pp->base < start) { | ||
266 | rb_prev = __rb_parent; | ||
267 | __rb_link = &__rb_parent->rb_right; | ||
268 | } else { | ||
269 | __rb_link = &__rb_parent->rb_left; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | *rb_link = __rb_link; | ||
274 | *rb_parent = __rb_parent; | ||
275 | *pprev = NULL; | ||
276 | if (rb_prev) | ||
277 | *pprev = rb_entry(rb_prev, struct ia64_partial_page, pp_rb); | ||
278 | return NULL; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * insert @pp into @ppl. | ||
283 | */ | ||
284 | static void | ||
285 | __ia32_insert_pp(struct ia64_partial_page_list *ppl, | ||
286 | struct ia64_partial_page *pp, struct ia64_partial_page *prev, | ||
287 | struct rb_node **rb_link, struct rb_node *rb_parent) | ||
288 | { | ||
289 | /* link list */ | ||
290 | if (prev) { | ||
291 | pp->next = prev->next; | ||
292 | prev->next = pp; | ||
293 | } else { | ||
294 | ppl->pp_head = pp; | ||
295 | if (rb_parent) | ||
296 | pp->next = rb_entry(rb_parent, | ||
297 | struct ia64_partial_page, pp_rb); | ||
298 | else | ||
299 | pp->next = NULL; | ||
300 | } | ||
301 | |||
302 | /* link rb */ | ||
303 | rb_link_node(&pp->pp_rb, rb_parent, rb_link); | ||
304 | rb_insert_color(&pp->pp_rb, &ppl->ppl_rb); | ||
305 | |||
306 | ppl->pp_hint = pp; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * delete @pp from partial page list @ppl. | ||
311 | */ | ||
312 | static void | ||
313 | __ia32_delete_pp(struct ia64_partial_page_list *ppl, | ||
314 | struct ia64_partial_page *pp, struct ia64_partial_page *prev) | ||
315 | { | ||
316 | if (prev) { | ||
317 | prev->next = pp->next; | ||
318 | if (ppl->pp_hint == pp) | ||
319 | ppl->pp_hint = prev; | ||
320 | } else { | ||
321 | ppl->pp_head = pp->next; | ||
322 | if (ppl->pp_hint == pp) | ||
323 | ppl->pp_hint = pp->next; | ||
324 | } | ||
325 | rb_erase(&pp->pp_rb, &ppl->ppl_rb); | ||
326 | kmem_cache_free(ia64_partial_page_cachep, pp); | ||
327 | } | ||
328 | |||
329 | static struct ia64_partial_page * | ||
330 | __pp_prev(struct ia64_partial_page *pp) | ||
331 | { | ||
332 | struct rb_node *prev = rb_prev(&pp->pp_rb); | ||
333 | if (prev) | ||
334 | return rb_entry(prev, struct ia64_partial_page, pp_rb); | ||
335 | else | ||
336 | return NULL; | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * Delete partial pages with address between @start and @end. | ||
341 | * @start and @end are page aligned. | ||
342 | */ | ||
343 | static void | ||
344 | __ia32_delete_pp_range(unsigned int start, unsigned int end) | ||
345 | { | ||
346 | struct ia64_partial_page *pp, *prev; | ||
347 | struct rb_node **rb_link, *rb_parent; | ||
348 | |||
349 | if (start >= end) | ||
350 | return; | ||
351 | |||
352 | pp = __ia32_find_pp(current->thread.ppl, start, &prev, | ||
353 | &rb_link, &rb_parent); | ||
354 | if (pp) | ||
355 | prev = __pp_prev(pp); | ||
356 | else { | ||
357 | if (prev) | ||
358 | pp = prev->next; | ||
359 | else | ||
360 | pp = current->thread.ppl->pp_head; | ||
361 | } | ||
362 | |||
363 | while (pp && pp->base < end) { | ||
364 | struct ia64_partial_page *tmp = pp->next; | ||
365 | __ia32_delete_pp(current->thread.ppl, pp, prev); | ||
366 | pp = tmp; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Set the range between @start and @end in bitmap. | ||
372 | * @start and @end should be IA32 page aligned and in the same IA64 page. | ||
373 | */ | ||
374 | static int | ||
375 | __ia32_set_pp(unsigned int start, unsigned int end, int flags) | ||
376 | { | ||
377 | struct ia64_partial_page *pp, *prev; | ||
378 | struct rb_node ** rb_link, *rb_parent; | ||
379 | unsigned int pstart, start_bit, end_bit, i; | ||
380 | |||
381 | pstart = PAGE_START(start); | ||
382 | start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
383 | end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
384 | if (end_bit == 0) | ||
385 | end_bit = PAGE_SIZE / IA32_PAGE_SIZE; | ||
386 | pp = __ia32_find_pp(current->thread.ppl, pstart, &prev, | ||
387 | &rb_link, &rb_parent); | ||
388 | if (pp) { | ||
389 | for (i = start_bit; i < end_bit; i++) | ||
390 | set_bit(i, &pp->bitmap); | ||
391 | /* | ||
392 | * Check: if this partial page has been set to a full page, | ||
393 | * then delete it. | ||
394 | */ | ||
395 | if (find_first_zero_bit(&pp->bitmap, sizeof(pp->bitmap)*8) >= | ||
396 | PAGE_SIZE/IA32_PAGE_SIZE) { | ||
397 | __ia32_delete_pp(current->thread.ppl, pp, __pp_prev(pp)); | ||
398 | } | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * MAP_FIXED may lead to overlapping mmap. | ||
404 | * In this case, the requested mmap area may already mmaped as a full | ||
405 | * page. So check vma before adding a new partial page. | ||
406 | */ | ||
407 | if (flags & MAP_FIXED) { | ||
408 | struct vm_area_struct *vma = find_vma(current->mm, pstart); | ||
409 | if (vma && vma->vm_start <= pstart) | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | /* new a ia64_partial_page */ | ||
414 | pp = kmem_cache_alloc(ia64_partial_page_cachep, GFP_KERNEL); | ||
415 | if (!pp) | ||
416 | return -ENOMEM; | ||
417 | pp->base = pstart; | ||
418 | pp->bitmap = 0; | ||
419 | for (i=start_bit; i<end_bit; i++) | ||
420 | set_bit(i, &(pp->bitmap)); | ||
421 | pp->next = NULL; | ||
422 | __ia32_insert_pp(current->thread.ppl, pp, prev, rb_link, rb_parent); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * @start and @end should be IA32 page aligned, but don't need to be in the | ||
428 | * same IA64 page. Split @start and @end to make sure they're in the same IA64 | ||
429 | * page, then call __ia32_set_pp(). | ||
430 | */ | ||
431 | static void | ||
432 | ia32_set_pp(unsigned int start, unsigned int end, int flags) | ||
433 | { | ||
434 | down_write(¤t->mm->mmap_sem); | ||
435 | if (flags & MAP_FIXED) { | ||
436 | /* | ||
437 | * MAP_FIXED may lead to overlapping mmap. When this happens, | ||
438 | * a series of complete IA64 pages results in deletion of | ||
439 | * old partial pages in that range. | ||
440 | */ | ||
441 | __ia32_delete_pp_range(PAGE_ALIGN(start), PAGE_START(end)); | ||
442 | } | ||
443 | |||
444 | if (end < PAGE_ALIGN(start)) { | ||
445 | __ia32_set_pp(start, end, flags); | ||
446 | } else { | ||
447 | if (offset_in_page(start)) | ||
448 | __ia32_set_pp(start, PAGE_ALIGN(start), flags); | ||
449 | if (offset_in_page(end)) | ||
450 | __ia32_set_pp(PAGE_START(end), end, flags); | ||
451 | } | ||
452 | up_write(¤t->mm->mmap_sem); | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * Unset the range between @start and @end in bitmap. | ||
457 | * @start and @end should be IA32 page aligned and in the same IA64 page. | ||
458 | * After doing that, if the bitmap is 0, then free the page and return 1, | ||
459 | * else return 0; | ||
460 | * If not find the partial page in the list, then | ||
461 | * If the vma exists, then the full page is set to a partial page; | ||
462 | * Else return -ENOMEM. | ||
463 | */ | ||
464 | static int | ||
465 | __ia32_unset_pp(unsigned int start, unsigned int end) | ||
466 | { | ||
467 | struct ia64_partial_page *pp, *prev; | ||
468 | struct rb_node ** rb_link, *rb_parent; | ||
469 | unsigned int pstart, start_bit, end_bit, i; | ||
470 | struct vm_area_struct *vma; | ||
471 | |||
472 | pstart = PAGE_START(start); | ||
473 | start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
474 | end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
475 | if (end_bit == 0) | ||
476 | end_bit = PAGE_SIZE / IA32_PAGE_SIZE; | ||
477 | |||
478 | pp = __ia32_find_pp(current->thread.ppl, pstart, &prev, | ||
479 | &rb_link, &rb_parent); | ||
480 | if (pp) { | ||
481 | for (i = start_bit; i < end_bit; i++) | ||
482 | clear_bit(i, &pp->bitmap); | ||
483 | if (pp->bitmap == 0) { | ||
484 | __ia32_delete_pp(current->thread.ppl, pp, __pp_prev(pp)); | ||
485 | return 1; | ||
486 | } | ||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | vma = find_vma(current->mm, pstart); | ||
491 | if (!vma || vma->vm_start > pstart) { | ||
492 | return -ENOMEM; | ||
493 | } | ||
494 | |||
495 | /* new a ia64_partial_page */ | ||
496 | pp = kmem_cache_alloc(ia64_partial_page_cachep, GFP_KERNEL); | ||
497 | if (!pp) | ||
498 | return -ENOMEM; | ||
499 | pp->base = pstart; | ||
500 | pp->bitmap = 0; | ||
501 | for (i = 0; i < start_bit; i++) | ||
502 | set_bit(i, &(pp->bitmap)); | ||
503 | for (i = end_bit; i < PAGE_SIZE / IA32_PAGE_SIZE; i++) | ||
504 | set_bit(i, &(pp->bitmap)); | ||
505 | pp->next = NULL; | ||
506 | __ia32_insert_pp(current->thread.ppl, pp, prev, rb_link, rb_parent); | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | /* | ||
511 | * Delete pp between PAGE_ALIGN(start) and PAGE_START(end) by calling | ||
512 | * __ia32_delete_pp_range(). Unset possible partial pages by calling | ||
513 | * __ia32_unset_pp(). | ||
514 | * The returned value see __ia32_unset_pp(). | ||
515 | */ | ||
516 | static int | ||
517 | ia32_unset_pp(unsigned int *startp, unsigned int *endp) | ||
518 | { | ||
519 | unsigned int start = *startp, end = *endp; | ||
520 | int ret = 0; | ||
521 | |||
522 | down_write(¤t->mm->mmap_sem); | ||
523 | |||
524 | __ia32_delete_pp_range(PAGE_ALIGN(start), PAGE_START(end)); | ||
525 | |||
526 | if (end < PAGE_ALIGN(start)) { | ||
527 | ret = __ia32_unset_pp(start, end); | ||
528 | if (ret == 1) { | ||
529 | *startp = PAGE_START(start); | ||
530 | *endp = PAGE_ALIGN(end); | ||
531 | } | ||
532 | if (ret == 0) { | ||
533 | /* to shortcut sys_munmap() in sys32_munmap() */ | ||
534 | *startp = PAGE_START(start); | ||
535 | *endp = PAGE_START(end); | ||
536 | } | ||
537 | } else { | ||
538 | if (offset_in_page(start)) { | ||
539 | ret = __ia32_unset_pp(start, PAGE_ALIGN(start)); | ||
540 | if (ret == 1) | ||
541 | *startp = PAGE_START(start); | ||
542 | if (ret == 0) | ||
543 | *startp = PAGE_ALIGN(start); | ||
544 | if (ret < 0) | ||
545 | goto out; | ||
546 | } | ||
547 | if (offset_in_page(end)) { | ||
548 | ret = __ia32_unset_pp(PAGE_START(end), end); | ||
549 | if (ret == 1) | ||
550 | *endp = PAGE_ALIGN(end); | ||
551 | if (ret == 0) | ||
552 | *endp = PAGE_START(end); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | out: | ||
557 | up_write(¤t->mm->mmap_sem); | ||
558 | return ret; | ||
559 | } | ||
560 | |||
561 | /* | ||
562 | * Compare the range between @start and @end with bitmap in partial page. | ||
563 | * @start and @end should be IA32 page aligned and in the same IA64 page. | ||
564 | */ | ||
565 | static int | ||
566 | __ia32_compare_pp(unsigned int start, unsigned int end) | ||
567 | { | ||
568 | struct ia64_partial_page *pp, *prev; | ||
569 | struct rb_node ** rb_link, *rb_parent; | ||
570 | unsigned int pstart, start_bit, end_bit, size; | ||
571 | unsigned int first_bit, next_zero_bit; /* the first range in bitmap */ | ||
572 | |||
573 | pstart = PAGE_START(start); | ||
574 | |||
575 | pp = __ia32_find_pp(current->thread.ppl, pstart, &prev, | ||
576 | &rb_link, &rb_parent); | ||
577 | if (!pp) | ||
578 | return 1; | ||
579 | |||
580 | start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
581 | end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; | ||
582 | size = sizeof(pp->bitmap) * 8; | ||
583 | first_bit = find_first_bit(&pp->bitmap, size); | ||
584 | next_zero_bit = find_next_zero_bit(&pp->bitmap, size, first_bit); | ||
585 | if ((start_bit < first_bit) || (end_bit > next_zero_bit)) { | ||
586 | /* exceeds the first range in bitmap */ | ||
587 | return -ENOMEM; | ||
588 | } else if ((start_bit == first_bit) && (end_bit == next_zero_bit)) { | ||
589 | first_bit = find_next_bit(&pp->bitmap, size, next_zero_bit); | ||
590 | if ((next_zero_bit < first_bit) && (first_bit < size)) | ||
591 | return 1; /* has next range */ | ||
592 | else | ||
593 | return 0; /* no next range */ | ||
594 | } else | ||
595 | return 1; | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | * @start and @end should be IA32 page aligned, but don't need to be in the | ||
600 | * same IA64 page. Split @start and @end to make sure they're in the same IA64 | ||
601 | * page, then call __ia32_compare_pp(). | ||
602 | * | ||
603 | * Take this as example: the range is the 1st and 2nd 4K page. | ||
604 | * Return 0 if they fit bitmap exactly, i.e. bitmap = 00000011; | ||
605 | * Return 1 if the range doesn't cover whole bitmap, e.g. bitmap = 00001111; | ||
606 | * Return -ENOMEM if the range exceeds the bitmap, e.g. bitmap = 00000001 or | ||
607 | * bitmap = 00000101. | ||
608 | */ | ||
609 | static int | ||
610 | ia32_compare_pp(unsigned int *startp, unsigned int *endp) | ||
611 | { | ||
612 | unsigned int start = *startp, end = *endp; | ||
613 | int retval = 0; | ||
614 | |||
615 | down_write(¤t->mm->mmap_sem); | ||
616 | |||
617 | if (end < PAGE_ALIGN(start)) { | ||
618 | retval = __ia32_compare_pp(start, end); | ||
619 | if (retval == 0) { | ||
620 | *startp = PAGE_START(start); | ||
621 | *endp = PAGE_ALIGN(end); | ||
622 | } | ||
623 | } else { | ||
624 | if (offset_in_page(start)) { | ||
625 | retval = __ia32_compare_pp(start, | ||
626 | PAGE_ALIGN(start)); | ||
627 | if (retval == 0) | ||
628 | *startp = PAGE_START(start); | ||
629 | if (retval < 0) | ||
630 | goto out; | ||
631 | } | ||
632 | if (offset_in_page(end)) { | ||
633 | retval = __ia32_compare_pp(PAGE_START(end), end); | ||
634 | if (retval == 0) | ||
635 | *endp = PAGE_ALIGN(end); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | out: | ||
640 | up_write(¤t->mm->mmap_sem); | ||
641 | return retval; | ||
642 | } | ||
643 | |||
644 | static void | ||
645 | __ia32_drop_pp_list(struct ia64_partial_page_list *ppl) | ||
646 | { | ||
647 | struct ia64_partial_page *pp = ppl->pp_head; | ||
648 | |||
649 | while (pp) { | ||
650 | struct ia64_partial_page *next = pp->next; | ||
651 | kmem_cache_free(ia64_partial_page_cachep, pp); | ||
652 | pp = next; | ||
653 | } | ||
654 | |||
655 | kfree(ppl); | ||
656 | } | ||
657 | |||
658 | void | ||
659 | ia32_drop_ia64_partial_page_list(struct task_struct *task) | ||
660 | { | ||
661 | struct ia64_partial_page_list* ppl = task->thread.ppl; | ||
662 | |||
663 | if (ppl && atomic_dec_and_test(&ppl->pp_count)) | ||
664 | __ia32_drop_pp_list(ppl); | ||
665 | } | ||
666 | |||
667 | /* | ||
668 | * Copy current->thread.ppl to ppl (already initialized). | ||
669 | */ | ||
670 | static int | ||
671 | __ia32_copy_pp_list(struct ia64_partial_page_list *ppl) | ||
672 | { | ||
673 | struct ia64_partial_page *pp, *tmp, *prev; | ||
674 | struct rb_node **rb_link, *rb_parent; | ||
675 | |||
676 | ppl->pp_head = NULL; | ||
677 | ppl->pp_hint = NULL; | ||
678 | ppl->ppl_rb = RB_ROOT; | ||
679 | rb_link = &ppl->ppl_rb.rb_node; | ||
680 | rb_parent = NULL; | ||
681 | prev = NULL; | ||
682 | |||
683 | for (pp = current->thread.ppl->pp_head; pp; pp = pp->next) { | ||
684 | tmp = kmem_cache_alloc(ia64_partial_page_cachep, GFP_KERNEL); | ||
685 | if (!tmp) | ||
686 | return -ENOMEM; | ||
687 | *tmp = *pp; | ||
688 | __ia32_insert_pp(ppl, tmp, prev, rb_link, rb_parent); | ||
689 | prev = tmp; | ||
690 | rb_link = &tmp->pp_rb.rb_right; | ||
691 | rb_parent = &tmp->pp_rb; | ||
692 | } | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | int | ||
697 | ia32_copy_ia64_partial_page_list(struct task_struct *p, | ||
698 | unsigned long clone_flags) | ||
699 | { | ||
700 | int retval = 0; | ||
701 | |||
702 | if (clone_flags & CLONE_VM) { | ||
703 | atomic_inc(¤t->thread.ppl->pp_count); | ||
704 | p->thread.ppl = current->thread.ppl; | ||
705 | } else { | ||
706 | p->thread.ppl = ia32_init_pp_list(); | ||
707 | if (!p->thread.ppl) | ||
708 | return -ENOMEM; | ||
709 | down_write(¤t->mm->mmap_sem); | ||
710 | { | ||
711 | retval = __ia32_copy_pp_list(p->thread.ppl); | ||
712 | } | ||
713 | up_write(¤t->mm->mmap_sem); | ||
714 | } | ||
715 | |||
716 | return retval; | ||
717 | } | ||
718 | |||
719 | static unsigned long | ||
720 | emulate_mmap (struct file *file, unsigned long start, unsigned long len, int prot, int flags, | ||
721 | loff_t off) | ||
722 | { | ||
723 | unsigned long tmp, end, pend, pstart, ret, is_congruent, fudge = 0; | ||
724 | struct inode *inode; | ||
725 | loff_t poff; | ||
726 | |||
727 | end = start + len; | ||
728 | pstart = PAGE_START(start); | ||
729 | pend = PAGE_ALIGN(end); | ||
730 | |||
731 | if (flags & MAP_FIXED) { | ||
732 | ia32_set_pp((unsigned int)start, (unsigned int)end, flags); | ||
733 | if (start > pstart) { | ||
734 | if (flags & MAP_SHARED) | ||
735 | printk(KERN_INFO | ||
736 | "%s(%d): emulate_mmap() can't share head (addr=0x%lx)\n", | ||
737 | current->comm, task_pid_nr(current), start); | ||
738 | ret = mmap_subpage(file, start, min(PAGE_ALIGN(start), end), prot, flags, | ||
739 | off); | ||
740 | if (IS_ERR((void *) ret)) | ||
741 | return ret; | ||
742 | pstart += PAGE_SIZE; | ||
743 | if (pstart >= pend) | ||
744 | goto out; /* done */ | ||
745 | } | ||
746 | if (end < pend) { | ||
747 | if (flags & MAP_SHARED) | ||
748 | printk(KERN_INFO | ||
749 | "%s(%d): emulate_mmap() can't share tail (end=0x%lx)\n", | ||
750 | current->comm, task_pid_nr(current), end); | ||
751 | ret = mmap_subpage(file, max(start, PAGE_START(end)), end, prot, flags, | ||
752 | (off + len) - offset_in_page(end)); | ||
753 | if (IS_ERR((void *) ret)) | ||
754 | return ret; | ||
755 | pend -= PAGE_SIZE; | ||
756 | if (pstart >= pend) | ||
757 | goto out; /* done */ | ||
758 | } | ||
759 | } else { | ||
760 | /* | ||
761 | * If a start address was specified, use it if the entire rounded out area | ||
762 | * is available. | ||
763 | */ | ||
764 | if (start && !pstart) | ||
765 | fudge = 1; /* handle case of mapping to range (0,PAGE_SIZE) */ | ||
766 | tmp = arch_get_unmapped_area(file, pstart - fudge, pend - pstart, 0, flags); | ||
767 | if (tmp != pstart) { | ||
768 | pstart = tmp; | ||
769 | start = pstart + offset_in_page(off); /* make start congruent with off */ | ||
770 | end = start + len; | ||
771 | pend = PAGE_ALIGN(end); | ||
772 | } | ||
773 | } | ||
774 | |||
775 | poff = off + (pstart - start); /* note: (pstart - start) may be negative */ | ||
776 | is_congruent = (flags & MAP_ANONYMOUS) || (offset_in_page(poff) == 0); | ||
777 | |||
778 | if ((flags & MAP_SHARED) && !is_congruent) | ||
779 | printk(KERN_INFO "%s(%d): emulate_mmap() can't share contents of incongruent mmap " | ||
780 | "(addr=0x%lx,off=0x%llx)\n", current->comm, task_pid_nr(current), start, off); | ||
781 | |||
782 | DBG("mmap_body: mapping [0x%lx-0x%lx) %s with poff 0x%llx\n", pstart, pend, | ||
783 | is_congruent ? "congruent" : "not congruent", poff); | ||
784 | |||
785 | down_write(¤t->mm->mmap_sem); | ||
786 | { | ||
787 | if (!(flags & MAP_ANONYMOUS) && is_congruent) | ||
788 | ret = do_mmap(file, pstart, pend - pstart, prot, flags | MAP_FIXED, poff); | ||
789 | else | ||
790 | ret = do_mmap(NULL, pstart, pend - pstart, | ||
791 | prot | ((flags & MAP_ANONYMOUS) ? 0 : PROT_WRITE), | ||
792 | flags | MAP_FIXED | MAP_ANONYMOUS, 0); | ||
793 | } | ||
794 | up_write(¤t->mm->mmap_sem); | ||
795 | |||
796 | if (IS_ERR((void *) ret)) | ||
797 | return ret; | ||
798 | |||
799 | if (!is_congruent) { | ||
800 | /* read the file contents */ | ||
801 | inode = file->f_path.dentry->d_inode; | ||
802 | if (!inode->i_fop || !file->f_op->read | ||
803 | || ((*file->f_op->read)(file, (char __user *) pstart, pend - pstart, &poff) | ||
804 | < 0)) | ||
805 | { | ||
806 | sys_munmap(pstart, pend - pstart); | ||
807 | return -EINVAL; | ||
808 | } | ||
809 | if (!(prot & PROT_WRITE) && sys_mprotect(pstart, pend - pstart, prot) < 0) | ||
810 | return -EINVAL; | ||
811 | } | ||
812 | |||
813 | if (!(flags & MAP_FIXED)) | ||
814 | ia32_set_pp((unsigned int)start, (unsigned int)end, flags); | ||
815 | out: | ||
816 | return start; | ||
817 | } | ||
818 | |||
819 | #endif /* PAGE_SHIFT > IA32_PAGE_SHIFT */ | ||
820 | |||
821 | static inline unsigned int | ||
822 | get_prot32 (unsigned int prot) | ||
823 | { | ||
824 | if (prot & PROT_WRITE) | ||
825 | /* on x86, PROT_WRITE implies PROT_READ which implies PROT_EEC */ | ||
826 | prot |= PROT_READ | PROT_WRITE | PROT_EXEC; | ||
827 | else if (prot & (PROT_READ | PROT_EXEC)) | ||
828 | /* on x86, there is no distinction between PROT_READ and PROT_EXEC */ | ||
829 | prot |= (PROT_READ | PROT_EXEC); | ||
830 | |||
831 | return prot; | ||
832 | } | ||
833 | |||
834 | unsigned long | ||
835 | ia32_do_mmap (struct file *file, unsigned long addr, unsigned long len, int prot, int flags, | ||
836 | loff_t offset) | ||
837 | { | ||
838 | DBG("ia32_do_mmap(file=%p,addr=0x%lx,len=0x%lx,prot=%x,flags=%x,offset=0x%llx)\n", | ||
839 | file, addr, len, prot, flags, offset); | ||
840 | |||
841 | if (file && (!file->f_op || !file->f_op->mmap)) | ||
842 | return -ENODEV; | ||
843 | |||
844 | len = IA32_PAGE_ALIGN(len); | ||
845 | if (len == 0) | ||
846 | return addr; | ||
847 | |||
848 | if (len > IA32_PAGE_OFFSET || addr > IA32_PAGE_OFFSET - len) | ||
849 | { | ||
850 | if (flags & MAP_FIXED) | ||
851 | return -ENOMEM; | ||
852 | else | ||
853 | return -EINVAL; | ||
854 | } | ||
855 | |||
856 | if (OFFSET4K(offset)) | ||
857 | return -EINVAL; | ||
858 | |||
859 | prot = get_prot32(prot); | ||
860 | |||
861 | if (flags & MAP_HUGETLB) | ||
862 | return -ENOMEM; | ||
863 | |||
864 | #if PAGE_SHIFT > IA32_PAGE_SHIFT | ||
865 | mutex_lock(&ia32_mmap_mutex); | ||
866 | { | ||
867 | addr = emulate_mmap(file, addr, len, prot, flags, offset); | ||
868 | } | ||
869 | mutex_unlock(&ia32_mmap_mutex); | ||
870 | #else | ||
871 | down_write(¤t->mm->mmap_sem); | ||
872 | { | ||
873 | addr = do_mmap(file, addr, len, prot, flags, offset); | ||
874 | } | ||
875 | up_write(¤t->mm->mmap_sem); | ||
876 | #endif | ||
877 | DBG("ia32_do_mmap: returning 0x%lx\n", addr); | ||
878 | return addr; | ||
879 | } | ||
880 | |||
881 | /* | ||
882 | * Linux/i386 didn't use to be able to handle more than 4 system call parameters, so these | ||
883 | * system calls used a memory block for parameter passing.. | ||
884 | */ | ||
885 | |||
886 | struct mmap_arg_struct { | ||
887 | unsigned int addr; | ||
888 | unsigned int len; | ||
889 | unsigned int prot; | ||
890 | unsigned int flags; | ||
891 | unsigned int fd; | ||
892 | unsigned int offset; | ||
893 | }; | ||
894 | |||
895 | asmlinkage long | ||
896 | sys32_mmap (struct mmap_arg_struct __user *arg) | ||
897 | { | ||
898 | struct mmap_arg_struct a; | ||
899 | struct file *file = NULL; | ||
900 | unsigned long addr; | ||
901 | int flags; | ||
902 | |||
903 | if (copy_from_user(&a, arg, sizeof(a))) | ||
904 | return -EFAULT; | ||
905 | |||
906 | if (OFFSET4K(a.offset)) | ||
907 | return -EINVAL; | ||
908 | |||
909 | flags = a.flags; | ||
910 | |||
911 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
912 | if (!(flags & MAP_ANONYMOUS)) { | ||
913 | file = fget(a.fd); | ||
914 | if (!file) | ||
915 | return -EBADF; | ||
916 | } | ||
917 | |||
918 | addr = ia32_do_mmap(file, a.addr, a.len, a.prot, flags, a.offset); | ||
919 | |||
920 | if (file) | ||
921 | fput(file); | ||
922 | return addr; | ||
923 | } | ||
924 | |||
925 | asmlinkage long | ||
926 | sys32_mmap2 (unsigned int addr, unsigned int len, unsigned int prot, unsigned int flags, | ||
927 | unsigned int fd, unsigned int pgoff) | ||
928 | { | ||
929 | struct file *file = NULL; | ||
930 | unsigned long retval; | ||
931 | |||
932 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
933 | if (!(flags & MAP_ANONYMOUS)) { | ||
934 | file = fget(fd); | ||
935 | if (!file) | ||
936 | return -EBADF; | ||
937 | } | ||
938 | |||
939 | retval = ia32_do_mmap(file, addr, len, prot, flags, | ||
940 | (unsigned long) pgoff << IA32_PAGE_SHIFT); | ||
941 | |||
942 | if (file) | ||
943 | fput(file); | ||
944 | return retval; | ||
945 | } | ||
946 | |||
947 | asmlinkage long | ||
948 | sys32_munmap (unsigned int start, unsigned int len) | ||
949 | { | ||
950 | unsigned int end = start + len; | ||
951 | long ret; | ||
952 | |||
953 | #if PAGE_SHIFT <= IA32_PAGE_SHIFT | ||
954 | ret = sys_munmap(start, end - start); | ||
955 | #else | ||
956 | if (OFFSET4K(start)) | ||
957 | return -EINVAL; | ||
958 | |||
959 | end = IA32_PAGE_ALIGN(end); | ||
960 | if (start >= end) | ||
961 | return -EINVAL; | ||
962 | |||
963 | ret = ia32_unset_pp(&start, &end); | ||
964 | if (ret < 0) | ||
965 | return ret; | ||
966 | |||
967 | if (start >= end) | ||
968 | return 0; | ||
969 | |||
970 | mutex_lock(&ia32_mmap_mutex); | ||
971 | ret = sys_munmap(start, end - start); | ||
972 | mutex_unlock(&ia32_mmap_mutex); | ||
973 | #endif | ||
974 | return ret; | ||
975 | } | ||
976 | |||
977 | #if PAGE_SHIFT > IA32_PAGE_SHIFT | ||
978 | |||
979 | /* | ||
980 | * When mprotect()ing a partial page, we set the permission to the union of the old | ||
981 | * settings and the new settings. In other words, it's only possible to make access to a | ||
982 | * partial page less restrictive. | ||
983 | */ | ||
984 | static long | ||
985 | mprotect_subpage (unsigned long address, int new_prot) | ||
986 | { | ||
987 | int old_prot; | ||
988 | struct vm_area_struct *vma; | ||
989 | |||
990 | if (new_prot == PROT_NONE) | ||
991 | return 0; /* optimize case where nothing changes... */ | ||
992 | vma = find_vma(current->mm, address); | ||
993 | old_prot = get_page_prot(vma, address); | ||
994 | return sys_mprotect(address, PAGE_SIZE, new_prot | old_prot); | ||
995 | } | ||
996 | |||
997 | #endif /* PAGE_SHIFT > IA32_PAGE_SHIFT */ | ||
998 | |||
999 | asmlinkage long | ||
1000 | sys32_mprotect (unsigned int start, unsigned int len, int prot) | ||
1001 | { | ||
1002 | unsigned int end = start + len; | ||
1003 | #if PAGE_SHIFT > IA32_PAGE_SHIFT | ||
1004 | long retval = 0; | ||
1005 | #endif | ||
1006 | |||
1007 | prot = get_prot32(prot); | ||
1008 | |||
1009 | #if PAGE_SHIFT <= IA32_PAGE_SHIFT | ||
1010 | return sys_mprotect(start, end - start, prot); | ||
1011 | #else | ||
1012 | if (OFFSET4K(start)) | ||
1013 | return -EINVAL; | ||
1014 | |||
1015 | end = IA32_PAGE_ALIGN(end); | ||
1016 | if (end < start) | ||
1017 | return -EINVAL; | ||
1018 | |||
1019 | retval = ia32_compare_pp(&start, &end); | ||
1020 | |||
1021 | if (retval < 0) | ||
1022 | return retval; | ||
1023 | |||
1024 | mutex_lock(&ia32_mmap_mutex); | ||
1025 | { | ||
1026 | if (offset_in_page(start)) { | ||
1027 | /* start address is 4KB aligned but not page aligned. */ | ||
1028 | retval = mprotect_subpage(PAGE_START(start), prot); | ||
1029 | if (retval < 0) | ||
1030 | goto out; | ||
1031 | |||
1032 | start = PAGE_ALIGN(start); | ||
1033 | if (start >= end) | ||
1034 | goto out; /* retval is already zero... */ | ||
1035 | } | ||
1036 | |||
1037 | if (offset_in_page(end)) { | ||
1038 | /* end address is 4KB aligned but not page aligned. */ | ||
1039 | retval = mprotect_subpage(PAGE_START(end), prot); | ||
1040 | if (retval < 0) | ||
1041 | goto out; | ||
1042 | |||
1043 | end = PAGE_START(end); | ||
1044 | } | ||
1045 | retval = sys_mprotect(start, end - start, prot); | ||
1046 | } | ||
1047 | out: | ||
1048 | mutex_unlock(&ia32_mmap_mutex); | ||
1049 | return retval; | ||
1050 | #endif | ||
1051 | } | ||
1052 | |||
1053 | asmlinkage long | ||
1054 | sys32_mremap (unsigned int addr, unsigned int old_len, unsigned int new_len, | ||
1055 | unsigned int flags, unsigned int new_addr) | ||
1056 | { | ||
1057 | long ret; | ||
1058 | |||
1059 | #if PAGE_SHIFT <= IA32_PAGE_SHIFT | ||
1060 | ret = sys_mremap(addr, old_len, new_len, flags, new_addr); | ||
1061 | #else | ||
1062 | unsigned int old_end, new_end; | ||
1063 | |||
1064 | if (OFFSET4K(addr)) | ||
1065 | return -EINVAL; | ||
1066 | |||
1067 | old_len = IA32_PAGE_ALIGN(old_len); | ||
1068 | new_len = IA32_PAGE_ALIGN(new_len); | ||
1069 | old_end = addr + old_len; | ||
1070 | new_end = addr + new_len; | ||
1071 | |||
1072 | if (!new_len) | ||
1073 | return -EINVAL; | ||
1074 | |||
1075 | if ((flags & MREMAP_FIXED) && (OFFSET4K(new_addr))) | ||
1076 | return -EINVAL; | ||
1077 | |||
1078 | if (old_len >= new_len) { | ||
1079 | ret = sys32_munmap(addr + new_len, old_len - new_len); | ||
1080 | if (ret && old_len != new_len) | ||
1081 | return ret; | ||
1082 | ret = addr; | ||
1083 | if (!(flags & MREMAP_FIXED) || (new_addr == addr)) | ||
1084 | return ret; | ||
1085 | old_len = new_len; | ||
1086 | } | ||
1087 | |||
1088 | addr = PAGE_START(addr); | ||
1089 | old_len = PAGE_ALIGN(old_end) - addr; | ||
1090 | new_len = PAGE_ALIGN(new_end) - addr; | ||
1091 | |||
1092 | mutex_lock(&ia32_mmap_mutex); | ||
1093 | ret = sys_mremap(addr, old_len, new_len, flags, new_addr); | ||
1094 | mutex_unlock(&ia32_mmap_mutex); | ||
1095 | |||
1096 | if ((ret >= 0) && (old_len < new_len)) { | ||
1097 | /* mremap expanded successfully */ | ||
1098 | ia32_set_pp(old_end, new_end, flags); | ||
1099 | } | ||
1100 | #endif | ||
1101 | return ret; | ||
1102 | } | ||
1103 | |||
1104 | asmlinkage unsigned long | ||
1105 | sys32_alarm (unsigned int seconds) | ||
1106 | { | ||
1107 | return alarm_setitimer(seconds); | ||
1108 | } | ||
1109 | |||
1110 | struct sel_arg_struct { | ||
1111 | unsigned int n; | ||
1112 | unsigned int inp; | ||
1113 | unsigned int outp; | ||
1114 | unsigned int exp; | ||
1115 | unsigned int tvp; | ||
1116 | }; | ||
1117 | |||
1118 | asmlinkage long | ||
1119 | sys32_old_select (struct sel_arg_struct __user *arg) | ||
1120 | { | ||
1121 | struct sel_arg_struct a; | ||
1122 | |||
1123 | if (copy_from_user(&a, arg, sizeof(a))) | ||
1124 | return -EFAULT; | ||
1125 | return compat_sys_select(a.n, compat_ptr(a.inp), compat_ptr(a.outp), | ||
1126 | compat_ptr(a.exp), compat_ptr(a.tvp)); | ||
1127 | } | ||
1128 | |||
1129 | #define SEMOP 1 | ||
1130 | #define SEMGET 2 | ||
1131 | #define SEMCTL 3 | ||
1132 | #define SEMTIMEDOP 4 | ||
1133 | #define MSGSND 11 | ||
1134 | #define MSGRCV 12 | ||
1135 | #define MSGGET 13 | ||
1136 | #define MSGCTL 14 | ||
1137 | #define SHMAT 21 | ||
1138 | #define SHMDT 22 | ||
1139 | #define SHMGET 23 | ||
1140 | #define SHMCTL 24 | ||
1141 | |||
1142 | asmlinkage long | ||
1143 | sys32_ipc(u32 call, int first, int second, int third, u32 ptr, u32 fifth) | ||
1144 | { | ||
1145 | int version; | ||
1146 | |||
1147 | version = call >> 16; /* hack for backward compatibility */ | ||
1148 | call &= 0xffff; | ||
1149 | |||
1150 | switch (call) { | ||
1151 | case SEMTIMEDOP: | ||
1152 | if (fifth) | ||
1153 | return compat_sys_semtimedop(first, compat_ptr(ptr), | ||
1154 | second, compat_ptr(fifth)); | ||
1155 | /* else fall through for normal semop() */ | ||
1156 | case SEMOP: | ||
1157 | /* struct sembuf is the same on 32 and 64bit :)) */ | ||
1158 | return sys_semtimedop(first, compat_ptr(ptr), second, | ||
1159 | NULL); | ||
1160 | case SEMGET: | ||
1161 | return sys_semget(first, second, third); | ||
1162 | case SEMCTL: | ||
1163 | return compat_sys_semctl(first, second, third, compat_ptr(ptr)); | ||
1164 | |||
1165 | case MSGSND: | ||
1166 | return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); | ||
1167 | case MSGRCV: | ||
1168 | return compat_sys_msgrcv(first, second, fifth, third, version, compat_ptr(ptr)); | ||
1169 | case MSGGET: | ||
1170 | return sys_msgget((key_t) first, second); | ||
1171 | case MSGCTL: | ||
1172 | return compat_sys_msgctl(first, second, compat_ptr(ptr)); | ||
1173 | |||
1174 | case SHMAT: | ||
1175 | return compat_sys_shmat(first, second, third, version, compat_ptr(ptr)); | ||
1176 | break; | ||
1177 | case SHMDT: | ||
1178 | return sys_shmdt(compat_ptr(ptr)); | ||
1179 | case SHMGET: | ||
1180 | return sys_shmget(first, (unsigned)second, third); | ||
1181 | case SHMCTL: | ||
1182 | return compat_sys_shmctl(first, second, compat_ptr(ptr)); | ||
1183 | |||
1184 | default: | ||
1185 | return -ENOSYS; | ||
1186 | } | ||
1187 | return -EINVAL; | ||
1188 | } | ||
1189 | |||
1190 | asmlinkage long | ||
1191 | compat_sys_wait4 (compat_pid_t pid, compat_uint_t * stat_addr, int options, | ||
1192 | struct compat_rusage *ru); | ||
1193 | |||
1194 | asmlinkage long | ||
1195 | sys32_waitpid (int pid, unsigned int *stat_addr, int options) | ||
1196 | { | ||
1197 | return compat_sys_wait4(pid, stat_addr, options, NULL); | ||
1198 | } | ||
1199 | |||
1200 | /* | ||
1201 | * The order in which registers are stored in the ptrace regs structure | ||
1202 | */ | ||
1203 | #define PT_EBX 0 | ||
1204 | #define PT_ECX 1 | ||
1205 | #define PT_EDX 2 | ||
1206 | #define PT_ESI 3 | ||
1207 | #define PT_EDI 4 | ||
1208 | #define PT_EBP 5 | ||
1209 | #define PT_EAX 6 | ||
1210 | #define PT_DS 7 | ||
1211 | #define PT_ES 8 | ||
1212 | #define PT_FS 9 | ||
1213 | #define PT_GS 10 | ||
1214 | #define PT_ORIG_EAX 11 | ||
1215 | #define PT_EIP 12 | ||
1216 | #define PT_CS 13 | ||
1217 | #define PT_EFL 14 | ||
1218 | #define PT_UESP 15 | ||
1219 | #define PT_SS 16 | ||
1220 | |||
1221 | static unsigned int | ||
1222 | getreg (struct task_struct *child, int regno) | ||
1223 | { | ||
1224 | struct pt_regs *child_regs; | ||
1225 | |||
1226 | child_regs = task_pt_regs(child); | ||
1227 | switch (regno / sizeof(int)) { | ||
1228 | case PT_EBX: return child_regs->r11; | ||
1229 | case PT_ECX: return child_regs->r9; | ||
1230 | case PT_EDX: return child_regs->r10; | ||
1231 | case PT_ESI: return child_regs->r14; | ||
1232 | case PT_EDI: return child_regs->r15; | ||
1233 | case PT_EBP: return child_regs->r13; | ||
1234 | case PT_EAX: return child_regs->r8; | ||
1235 | case PT_ORIG_EAX: return child_regs->r1; /* see dispatch_to_ia32_handler() */ | ||
1236 | case PT_EIP: return child_regs->cr_iip; | ||
1237 | case PT_UESP: return child_regs->r12; | ||
1238 | case PT_EFL: return child->thread.eflag; | ||
1239 | case PT_DS: case PT_ES: case PT_FS: case PT_GS: case PT_SS: | ||
1240 | return __USER_DS; | ||
1241 | case PT_CS: return __USER_CS; | ||
1242 | default: | ||
1243 | printk(KERN_ERR "ia32.getreg(): unknown register %d\n", regno); | ||
1244 | break; | ||
1245 | } | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | static void | ||
1250 | putreg (struct task_struct *child, int regno, unsigned int value) | ||
1251 | { | ||
1252 | struct pt_regs *child_regs; | ||
1253 | |||
1254 | child_regs = task_pt_regs(child); | ||
1255 | switch (regno / sizeof(int)) { | ||
1256 | case PT_EBX: child_regs->r11 = value; break; | ||
1257 | case PT_ECX: child_regs->r9 = value; break; | ||
1258 | case PT_EDX: child_regs->r10 = value; break; | ||
1259 | case PT_ESI: child_regs->r14 = value; break; | ||
1260 | case PT_EDI: child_regs->r15 = value; break; | ||
1261 | case PT_EBP: child_regs->r13 = value; break; | ||
1262 | case PT_EAX: child_regs->r8 = value; break; | ||
1263 | case PT_ORIG_EAX: child_regs->r1 = value; break; | ||
1264 | case PT_EIP: child_regs->cr_iip = value; break; | ||
1265 | case PT_UESP: child_regs->r12 = value; break; | ||
1266 | case PT_EFL: child->thread.eflag = value; break; | ||
1267 | case PT_DS: case PT_ES: case PT_FS: case PT_GS: case PT_SS: | ||
1268 | if (value != __USER_DS) | ||
1269 | printk(KERN_ERR | ||
1270 | "ia32.putreg: attempt to set invalid segment register %d = %x\n", | ||
1271 | regno, value); | ||
1272 | break; | ||
1273 | case PT_CS: | ||
1274 | if (value != __USER_CS) | ||
1275 | printk(KERN_ERR | ||
1276 | "ia32.putreg: attempt to set invalid segment register %d = %x\n", | ||
1277 | regno, value); | ||
1278 | break; | ||
1279 | default: | ||
1280 | printk(KERN_ERR "ia32.putreg: unknown register %d\n", regno); | ||
1281 | break; | ||
1282 | } | ||
1283 | } | ||
1284 | |||
1285 | static void | ||
1286 | put_fpreg (int regno, struct _fpreg_ia32 __user *reg, struct pt_regs *ptp, | ||
1287 | struct switch_stack *swp, int tos) | ||
1288 | { | ||
1289 | struct _fpreg_ia32 *f; | ||
1290 | char buf[32]; | ||
1291 | |||
1292 | f = (struct _fpreg_ia32 *)(((unsigned long)buf + 15) & ~15); | ||
1293 | if ((regno += tos) >= 8) | ||
1294 | regno -= 8; | ||
1295 | switch (regno) { | ||
1296 | case 0: | ||
1297 | ia64f2ia32f(f, &ptp->f8); | ||
1298 | break; | ||
1299 | case 1: | ||
1300 | ia64f2ia32f(f, &ptp->f9); | ||
1301 | break; | ||
1302 | case 2: | ||
1303 | ia64f2ia32f(f, &ptp->f10); | ||
1304 | break; | ||
1305 | case 3: | ||
1306 | ia64f2ia32f(f, &ptp->f11); | ||
1307 | break; | ||
1308 | case 4: | ||
1309 | case 5: | ||
1310 | case 6: | ||
1311 | case 7: | ||
1312 | ia64f2ia32f(f, &swp->f12 + (regno - 4)); | ||
1313 | break; | ||
1314 | } | ||
1315 | copy_to_user(reg, f, sizeof(*reg)); | ||
1316 | } | ||
1317 | |||
1318 | static void | ||
1319 | get_fpreg (int regno, struct _fpreg_ia32 __user *reg, struct pt_regs *ptp, | ||
1320 | struct switch_stack *swp, int tos) | ||
1321 | { | ||
1322 | |||
1323 | if ((regno += tos) >= 8) | ||
1324 | regno -= 8; | ||
1325 | switch (regno) { | ||
1326 | case 0: | ||
1327 | copy_from_user(&ptp->f8, reg, sizeof(*reg)); | ||
1328 | break; | ||
1329 | case 1: | ||
1330 | copy_from_user(&ptp->f9, reg, sizeof(*reg)); | ||
1331 | break; | ||
1332 | case 2: | ||
1333 | copy_from_user(&ptp->f10, reg, sizeof(*reg)); | ||
1334 | break; | ||
1335 | case 3: | ||
1336 | copy_from_user(&ptp->f11, reg, sizeof(*reg)); | ||
1337 | break; | ||
1338 | case 4: | ||
1339 | case 5: | ||
1340 | case 6: | ||
1341 | case 7: | ||
1342 | copy_from_user(&swp->f12 + (regno - 4), reg, sizeof(*reg)); | ||
1343 | break; | ||
1344 | } | ||
1345 | return; | ||
1346 | } | ||
1347 | |||
1348 | int | ||
1349 | save_ia32_fpstate (struct task_struct *tsk, struct ia32_user_i387_struct __user *save) | ||
1350 | { | ||
1351 | struct switch_stack *swp; | ||
1352 | struct pt_regs *ptp; | ||
1353 | int i, tos; | ||
1354 | |||
1355 | if (!access_ok(VERIFY_WRITE, save, sizeof(*save))) | ||
1356 | return -EFAULT; | ||
1357 | |||
1358 | __put_user(tsk->thread.fcr & 0xffff, &save->cwd); | ||
1359 | __put_user(tsk->thread.fsr & 0xffff, &save->swd); | ||
1360 | __put_user((tsk->thread.fsr>>16) & 0xffff, &save->twd); | ||
1361 | __put_user(tsk->thread.fir, &save->fip); | ||
1362 | __put_user((tsk->thread.fir>>32) & 0xffff, &save->fcs); | ||
1363 | __put_user(tsk->thread.fdr, &save->foo); | ||
1364 | __put_user((tsk->thread.fdr>>32) & 0xffff, &save->fos); | ||
1365 | |||
1366 | /* | ||
1367 | * Stack frames start with 16-bytes of temp space | ||
1368 | */ | ||
1369 | swp = (struct switch_stack *)(tsk->thread.ksp + 16); | ||
1370 | ptp = task_pt_regs(tsk); | ||
1371 | tos = (tsk->thread.fsr >> 11) & 7; | ||
1372 | for (i = 0; i < 8; i++) | ||
1373 | put_fpreg(i, &save->st_space[i], ptp, swp, tos); | ||
1374 | return 0; | ||
1375 | } | ||
1376 | |||
1377 | static int | ||
1378 | restore_ia32_fpstate (struct task_struct *tsk, struct ia32_user_i387_struct __user *save) | ||
1379 | { | ||
1380 | struct switch_stack *swp; | ||
1381 | struct pt_regs *ptp; | ||
1382 | int i, tos; | ||
1383 | unsigned int fsrlo, fsrhi, num32; | ||
1384 | |||
1385 | if (!access_ok(VERIFY_READ, save, sizeof(*save))) | ||
1386 | return(-EFAULT); | ||
1387 | |||
1388 | __get_user(num32, (unsigned int __user *)&save->cwd); | ||
1389 | tsk->thread.fcr = (tsk->thread.fcr & (~0x1f3f)) | (num32 & 0x1f3f); | ||
1390 | __get_user(fsrlo, (unsigned int __user *)&save->swd); | ||
1391 | __get_user(fsrhi, (unsigned int __user *)&save->twd); | ||
1392 | num32 = (fsrhi << 16) | fsrlo; | ||
1393 | tsk->thread.fsr = (tsk->thread.fsr & (~0xffffffff)) | num32; | ||
1394 | __get_user(num32, (unsigned int __user *)&save->fip); | ||
1395 | tsk->thread.fir = (tsk->thread.fir & (~0xffffffff)) | num32; | ||
1396 | __get_user(num32, (unsigned int __user *)&save->foo); | ||
1397 | tsk->thread.fdr = (tsk->thread.fdr & (~0xffffffff)) | num32; | ||
1398 | |||
1399 | /* | ||
1400 | * Stack frames start with 16-bytes of temp space | ||
1401 | */ | ||
1402 | swp = (struct switch_stack *)(tsk->thread.ksp + 16); | ||
1403 | ptp = task_pt_regs(tsk); | ||
1404 | tos = (tsk->thread.fsr >> 11) & 7; | ||
1405 | for (i = 0; i < 8; i++) | ||
1406 | get_fpreg(i, &save->st_space[i], ptp, swp, tos); | ||
1407 | return 0; | ||
1408 | } | ||
1409 | |||
1410 | int | ||
1411 | save_ia32_fpxstate (struct task_struct *tsk, struct ia32_user_fxsr_struct __user *save) | ||
1412 | { | ||
1413 | struct switch_stack *swp; | ||
1414 | struct pt_regs *ptp; | ||
1415 | int i, tos; | ||
1416 | unsigned long mxcsr=0; | ||
1417 | unsigned long num128[2]; | ||
1418 | |||
1419 | if (!access_ok(VERIFY_WRITE, save, sizeof(*save))) | ||
1420 | return -EFAULT; | ||
1421 | |||
1422 | __put_user(tsk->thread.fcr & 0xffff, &save->cwd); | ||
1423 | __put_user(tsk->thread.fsr & 0xffff, &save->swd); | ||
1424 | __put_user((tsk->thread.fsr>>16) & 0xffff, &save->twd); | ||
1425 | __put_user(tsk->thread.fir, &save->fip); | ||
1426 | __put_user((tsk->thread.fir>>32) & 0xffff, &save->fcs); | ||
1427 | __put_user(tsk->thread.fdr, &save->foo); | ||
1428 | __put_user((tsk->thread.fdr>>32) & 0xffff, &save->fos); | ||
1429 | |||
1430 | /* | ||
1431 | * Stack frames start with 16-bytes of temp space | ||
1432 | */ | ||
1433 | swp = (struct switch_stack *)(tsk->thread.ksp + 16); | ||
1434 | ptp = task_pt_regs(tsk); | ||
1435 | tos = (tsk->thread.fsr >> 11) & 7; | ||
1436 | for (i = 0; i < 8; i++) | ||
1437 | put_fpreg(i, (struct _fpreg_ia32 __user *)&save->st_space[4*i], ptp, swp, tos); | ||
1438 | |||
1439 | mxcsr = ((tsk->thread.fcr>>32) & 0xff80) | ((tsk->thread.fsr>>32) & 0x3f); | ||
1440 | __put_user(mxcsr & 0xffff, &save->mxcsr); | ||
1441 | for (i = 0; i < 8; i++) { | ||
1442 | memcpy(&(num128[0]), &(swp->f16) + i*2, sizeof(unsigned long)); | ||
1443 | memcpy(&(num128[1]), &(swp->f17) + i*2, sizeof(unsigned long)); | ||
1444 | copy_to_user(&save->xmm_space[0] + 4*i, num128, sizeof(struct _xmmreg_ia32)); | ||
1445 | } | ||
1446 | return 0; | ||
1447 | } | ||
1448 | |||
1449 | static int | ||
1450 | restore_ia32_fpxstate (struct task_struct *tsk, struct ia32_user_fxsr_struct __user *save) | ||
1451 | { | ||
1452 | struct switch_stack *swp; | ||
1453 | struct pt_regs *ptp; | ||
1454 | int i, tos; | ||
1455 | unsigned int fsrlo, fsrhi, num32; | ||
1456 | int mxcsr; | ||
1457 | unsigned long num64; | ||
1458 | unsigned long num128[2]; | ||
1459 | |||
1460 | if (!access_ok(VERIFY_READ, save, sizeof(*save))) | ||
1461 | return(-EFAULT); | ||
1462 | |||
1463 | __get_user(num32, (unsigned int __user *)&save->cwd); | ||
1464 | tsk->thread.fcr = (tsk->thread.fcr & (~0x1f3f)) | (num32 & 0x1f3f); | ||
1465 | __get_user(fsrlo, (unsigned int __user *)&save->swd); | ||
1466 | __get_user(fsrhi, (unsigned int __user *)&save->twd); | ||
1467 | num32 = (fsrhi << 16) | fsrlo; | ||
1468 | tsk->thread.fsr = (tsk->thread.fsr & (~0xffffffff)) | num32; | ||
1469 | __get_user(num32, (unsigned int __user *)&save->fip); | ||
1470 | tsk->thread.fir = (tsk->thread.fir & (~0xffffffff)) | num32; | ||
1471 | __get_user(num32, (unsigned int __user *)&save->foo); | ||
1472 | tsk->thread.fdr = (tsk->thread.fdr & (~0xffffffff)) | num32; | ||
1473 | |||
1474 | /* | ||
1475 | * Stack frames start with 16-bytes of temp space | ||
1476 | */ | ||
1477 | swp = (struct switch_stack *)(tsk->thread.ksp + 16); | ||
1478 | ptp = task_pt_regs(tsk); | ||
1479 | tos = (tsk->thread.fsr >> 11) & 7; | ||
1480 | for (i = 0; i < 8; i++) | ||
1481 | get_fpreg(i, (struct _fpreg_ia32 __user *)&save->st_space[4*i], ptp, swp, tos); | ||
1482 | |||
1483 | __get_user(mxcsr, (unsigned int __user *)&save->mxcsr); | ||
1484 | num64 = mxcsr & 0xff10; | ||
1485 | tsk->thread.fcr = (tsk->thread.fcr & (~0xff1000000000UL)) | (num64<<32); | ||
1486 | num64 = mxcsr & 0x3f; | ||
1487 | tsk->thread.fsr = (tsk->thread.fsr & (~0x3f00000000UL)) | (num64<<32); | ||
1488 | |||
1489 | for (i = 0; i < 8; i++) { | ||
1490 | copy_from_user(num128, &save->xmm_space[0] + 4*i, sizeof(struct _xmmreg_ia32)); | ||
1491 | memcpy(&(swp->f16) + i*2, &(num128[0]), sizeof(unsigned long)); | ||
1492 | memcpy(&(swp->f17) + i*2, &(num128[1]), sizeof(unsigned long)); | ||
1493 | } | ||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1497 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | ||
1498 | compat_ulong_t caddr, compat_ulong_t cdata) | ||
1499 | { | ||
1500 | unsigned long addr = caddr; | ||
1501 | unsigned long data = cdata; | ||
1502 | unsigned int tmp; | ||
1503 | long i, ret; | ||
1504 | |||
1505 | switch (request) { | ||
1506 | case PTRACE_PEEKUSR: /* read word at addr in USER area */ | ||
1507 | ret = -EIO; | ||
1508 | if ((addr & 3) || addr > 17*sizeof(int)) | ||
1509 | break; | ||
1510 | |||
1511 | tmp = getreg(child, addr); | ||
1512 | if (!put_user(tmp, (unsigned int __user *) compat_ptr(data))) | ||
1513 | ret = 0; | ||
1514 | break; | ||
1515 | |||
1516 | case PTRACE_POKEUSR: /* write word at addr in USER area */ | ||
1517 | ret = -EIO; | ||
1518 | if ((addr & 3) || addr > 17*sizeof(int)) | ||
1519 | break; | ||
1520 | |||
1521 | putreg(child, addr, data); | ||
1522 | ret = 0; | ||
1523 | break; | ||
1524 | |||
1525 | case IA32_PTRACE_GETREGS: | ||
1526 | if (!access_ok(VERIFY_WRITE, compat_ptr(data), 17*sizeof(int))) { | ||
1527 | ret = -EIO; | ||
1528 | break; | ||
1529 | } | ||
1530 | for (i = 0; i < (int) (17*sizeof(int)); i += sizeof(int) ) { | ||
1531 | put_user(getreg(child, i), (unsigned int __user *) compat_ptr(data)); | ||
1532 | data += sizeof(int); | ||
1533 | } | ||
1534 | ret = 0; | ||
1535 | break; | ||
1536 | |||
1537 | case IA32_PTRACE_SETREGS: | ||
1538 | if (!access_ok(VERIFY_READ, compat_ptr(data), 17*sizeof(int))) { | ||
1539 | ret = -EIO; | ||
1540 | break; | ||
1541 | } | ||
1542 | for (i = 0; i < (int) (17*sizeof(int)); i += sizeof(int) ) { | ||
1543 | get_user(tmp, (unsigned int __user *) compat_ptr(data)); | ||
1544 | putreg(child, i, tmp); | ||
1545 | data += sizeof(int); | ||
1546 | } | ||
1547 | ret = 0; | ||
1548 | break; | ||
1549 | |||
1550 | case IA32_PTRACE_GETFPREGS: | ||
1551 | ret = save_ia32_fpstate(child, (struct ia32_user_i387_struct __user *) | ||
1552 | compat_ptr(data)); | ||
1553 | break; | ||
1554 | |||
1555 | case IA32_PTRACE_GETFPXREGS: | ||
1556 | ret = save_ia32_fpxstate(child, (struct ia32_user_fxsr_struct __user *) | ||
1557 | compat_ptr(data)); | ||
1558 | break; | ||
1559 | |||
1560 | case IA32_PTRACE_SETFPREGS: | ||
1561 | ret = restore_ia32_fpstate(child, (struct ia32_user_i387_struct __user *) | ||
1562 | compat_ptr(data)); | ||
1563 | break; | ||
1564 | |||
1565 | case IA32_PTRACE_SETFPXREGS: | ||
1566 | ret = restore_ia32_fpxstate(child, (struct ia32_user_fxsr_struct __user *) | ||
1567 | compat_ptr(data)); | ||
1568 | break; | ||
1569 | |||
1570 | default: | ||
1571 | return compat_ptrace_request(child, request, caddr, cdata); | ||
1572 | } | ||
1573 | return ret; | ||
1574 | } | ||
1575 | |||
1576 | typedef struct { | ||
1577 | unsigned int ss_sp; | ||
1578 | unsigned int ss_flags; | ||
1579 | unsigned int ss_size; | ||
1580 | } ia32_stack_t; | ||
1581 | |||
1582 | asmlinkage long | ||
1583 | sys32_sigaltstack (ia32_stack_t __user *uss32, ia32_stack_t __user *uoss32, | ||
1584 | long arg2, long arg3, long arg4, long arg5, long arg6, | ||
1585 | long arg7, struct pt_regs pt) | ||
1586 | { | ||
1587 | stack_t uss, uoss; | ||
1588 | ia32_stack_t buf32; | ||
1589 | int ret; | ||
1590 | mm_segment_t old_fs = get_fs(); | ||
1591 | |||
1592 | if (uss32) { | ||
1593 | if (copy_from_user(&buf32, uss32, sizeof(ia32_stack_t))) | ||
1594 | return -EFAULT; | ||
1595 | uss.ss_sp = (void __user *) (long) buf32.ss_sp; | ||
1596 | uss.ss_flags = buf32.ss_flags; | ||
1597 | /* MINSIGSTKSZ is different for ia32 vs ia64. We lie here to pass the | ||
1598 | check and set it to the user requested value later */ | ||
1599 | if ((buf32.ss_flags != SS_DISABLE) && (buf32.ss_size < MINSIGSTKSZ_IA32)) { | ||
1600 | ret = -ENOMEM; | ||
1601 | goto out; | ||
1602 | } | ||
1603 | uss.ss_size = MINSIGSTKSZ; | ||
1604 | } | ||
1605 | set_fs(KERNEL_DS); | ||
1606 | ret = do_sigaltstack(uss32 ? (stack_t __user *) &uss : NULL, | ||
1607 | (stack_t __user *) &uoss, pt.r12); | ||
1608 | current->sas_ss_size = buf32.ss_size; | ||
1609 | set_fs(old_fs); | ||
1610 | out: | ||
1611 | if (ret < 0) | ||
1612 | return(ret); | ||
1613 | if (uoss32) { | ||
1614 | buf32.ss_sp = (long __user) uoss.ss_sp; | ||
1615 | buf32.ss_flags = uoss.ss_flags; | ||
1616 | buf32.ss_size = uoss.ss_size; | ||
1617 | if (copy_to_user(uoss32, &buf32, sizeof(ia32_stack_t))) | ||
1618 | return -EFAULT; | ||
1619 | } | ||
1620 | return ret; | ||
1621 | } | ||
1622 | |||
1623 | asmlinkage int | ||
1624 | sys32_msync (unsigned int start, unsigned int len, int flags) | ||
1625 | { | ||
1626 | unsigned int addr; | ||
1627 | |||
1628 | if (OFFSET4K(start)) | ||
1629 | return -EINVAL; | ||
1630 | addr = PAGE_START(start); | ||
1631 | return sys_msync(addr, len + (start - addr), flags); | ||
1632 | } | ||
1633 | |||
1634 | asmlinkage long | ||
1635 | sys32_newuname (struct new_utsname __user *name) | ||
1636 | { | ||
1637 | int ret = sys_newuname(name); | ||
1638 | |||
1639 | if (!ret) | ||
1640 | if (copy_to_user(name->machine, "i686\0\0\0", 8)) | ||
1641 | ret = -EFAULT; | ||
1642 | return ret; | ||
1643 | } | ||
1644 | |||
1645 | asmlinkage long | ||
1646 | sys32_getresuid16 (u16 __user *ruid, u16 __user *euid, u16 __user *suid) | ||
1647 | { | ||
1648 | uid_t a, b, c; | ||
1649 | int ret; | ||
1650 | mm_segment_t old_fs = get_fs(); | ||
1651 | |||
1652 | set_fs(KERNEL_DS); | ||
1653 | ret = sys_getresuid((uid_t __user *) &a, (uid_t __user *) &b, (uid_t __user *) &c); | ||
1654 | set_fs(old_fs); | ||
1655 | |||
1656 | if (put_user(a, ruid) || put_user(b, euid) || put_user(c, suid)) | ||
1657 | return -EFAULT; | ||
1658 | return ret; | ||
1659 | } | ||
1660 | |||
1661 | asmlinkage long | ||
1662 | sys32_getresgid16 (u16 __user *rgid, u16 __user *egid, u16 __user *sgid) | ||
1663 | { | ||
1664 | gid_t a, b, c; | ||
1665 | int ret; | ||
1666 | mm_segment_t old_fs = get_fs(); | ||
1667 | |||
1668 | set_fs(KERNEL_DS); | ||
1669 | ret = sys_getresgid((gid_t __user *) &a, (gid_t __user *) &b, (gid_t __user *) &c); | ||
1670 | set_fs(old_fs); | ||
1671 | |||
1672 | if (ret) | ||
1673 | return ret; | ||
1674 | |||
1675 | return put_user(a, rgid) | put_user(b, egid) | put_user(c, sgid); | ||
1676 | } | ||
1677 | |||
1678 | asmlinkage long | ||
1679 | sys32_lseek (unsigned int fd, int offset, unsigned int whence) | ||
1680 | { | ||
1681 | /* Sign-extension of "offset" is important here... */ | ||
1682 | return sys_lseek(fd, offset, whence); | ||
1683 | } | ||
1684 | |||
1685 | static int | ||
1686 | groups16_to_user(short __user *grouplist, struct group_info *group_info) | ||
1687 | { | ||
1688 | int i; | ||
1689 | short group; | ||
1690 | |||
1691 | for (i = 0; i < group_info->ngroups; i++) { | ||
1692 | group = (short)GROUP_AT(group_info, i); | ||
1693 | if (put_user(group, grouplist+i)) | ||
1694 | return -EFAULT; | ||
1695 | } | ||
1696 | |||
1697 | return 0; | ||
1698 | } | ||
1699 | |||
1700 | static int | ||
1701 | groups16_from_user(struct group_info *group_info, short __user *grouplist) | ||
1702 | { | ||
1703 | int i; | ||
1704 | short group; | ||
1705 | |||
1706 | for (i = 0; i < group_info->ngroups; i++) { | ||
1707 | if (get_user(group, grouplist+i)) | ||
1708 | return -EFAULT; | ||
1709 | GROUP_AT(group_info, i) = (gid_t)group; | ||
1710 | } | ||
1711 | |||
1712 | return 0; | ||
1713 | } | ||
1714 | |||
1715 | asmlinkage long | ||
1716 | sys32_getgroups16 (int gidsetsize, short __user *grouplist) | ||
1717 | { | ||
1718 | const struct cred *cred = current_cred(); | ||
1719 | int i; | ||
1720 | |||
1721 | if (gidsetsize < 0) | ||
1722 | return -EINVAL; | ||
1723 | |||
1724 | i = cred->group_info->ngroups; | ||
1725 | if (gidsetsize) { | ||
1726 | if (i > gidsetsize) { | ||
1727 | i = -EINVAL; | ||
1728 | goto out; | ||
1729 | } | ||
1730 | if (groups16_to_user(grouplist, cred->group_info)) { | ||
1731 | i = -EFAULT; | ||
1732 | goto out; | ||
1733 | } | ||
1734 | } | ||
1735 | out: | ||
1736 | return i; | ||
1737 | } | ||
1738 | |||
1739 | asmlinkage long | ||
1740 | sys32_setgroups16 (int gidsetsize, short __user *grouplist) | ||
1741 | { | ||
1742 | struct group_info *group_info; | ||
1743 | int retval; | ||
1744 | |||
1745 | if (!capable(CAP_SETGID)) | ||
1746 | return -EPERM; | ||
1747 | if ((unsigned)gidsetsize > NGROUPS_MAX) | ||
1748 | return -EINVAL; | ||
1749 | |||
1750 | group_info = groups_alloc(gidsetsize); | ||
1751 | if (!group_info) | ||
1752 | return -ENOMEM; | ||
1753 | retval = groups16_from_user(group_info, grouplist); | ||
1754 | if (retval) { | ||
1755 | put_group_info(group_info); | ||
1756 | return retval; | ||
1757 | } | ||
1758 | |||
1759 | retval = set_current_groups(group_info); | ||
1760 | put_group_info(group_info); | ||
1761 | |||
1762 | return retval; | ||
1763 | } | ||
1764 | |||
1765 | asmlinkage long | ||
1766 | sys32_truncate64 (unsigned int path, unsigned int len_lo, unsigned int len_hi) | ||
1767 | { | ||
1768 | return sys_truncate(compat_ptr(path), ((unsigned long) len_hi << 32) | len_lo); | ||
1769 | } | ||
1770 | |||
1771 | asmlinkage long | ||
1772 | sys32_ftruncate64 (int fd, unsigned int len_lo, unsigned int len_hi) | ||
1773 | { | ||
1774 | return sys_ftruncate(fd, ((unsigned long) len_hi << 32) | len_lo); | ||
1775 | } | ||
1776 | |||
1777 | static int | ||
1778 | putstat64 (struct stat64 __user *ubuf, struct kstat *kbuf) | ||
1779 | { | ||
1780 | int err; | ||
1781 | u64 hdev; | ||
1782 | |||
1783 | if (clear_user(ubuf, sizeof(*ubuf))) | ||
1784 | return -EFAULT; | ||
1785 | |||
1786 | hdev = huge_encode_dev(kbuf->dev); | ||
1787 | err = __put_user(hdev, (u32 __user*)&ubuf->st_dev); | ||
1788 | err |= __put_user(hdev >> 32, ((u32 __user*)&ubuf->st_dev) + 1); | ||
1789 | err |= __put_user(kbuf->ino, &ubuf->__st_ino); | ||
1790 | err |= __put_user(kbuf->ino, &ubuf->st_ino_lo); | ||
1791 | err |= __put_user(kbuf->ino >> 32, &ubuf->st_ino_hi); | ||
1792 | err |= __put_user(kbuf->mode, &ubuf->st_mode); | ||
1793 | err |= __put_user(kbuf->nlink, &ubuf->st_nlink); | ||
1794 | err |= __put_user(kbuf->uid, &ubuf->st_uid); | ||
1795 | err |= __put_user(kbuf->gid, &ubuf->st_gid); | ||
1796 | hdev = huge_encode_dev(kbuf->rdev); | ||
1797 | err = __put_user(hdev, (u32 __user*)&ubuf->st_rdev); | ||
1798 | err |= __put_user(hdev >> 32, ((u32 __user*)&ubuf->st_rdev) + 1); | ||
1799 | err |= __put_user(kbuf->size, &ubuf->st_size_lo); | ||
1800 | err |= __put_user((kbuf->size >> 32), &ubuf->st_size_hi); | ||
1801 | err |= __put_user(kbuf->atime.tv_sec, &ubuf->st_atime); | ||
1802 | err |= __put_user(kbuf->atime.tv_nsec, &ubuf->st_atime_nsec); | ||
1803 | err |= __put_user(kbuf->mtime.tv_sec, &ubuf->st_mtime); | ||
1804 | err |= __put_user(kbuf->mtime.tv_nsec, &ubuf->st_mtime_nsec); | ||
1805 | err |= __put_user(kbuf->ctime.tv_sec, &ubuf->st_ctime); | ||
1806 | err |= __put_user(kbuf->ctime.tv_nsec, &ubuf->st_ctime_nsec); | ||
1807 | err |= __put_user(kbuf->blksize, &ubuf->st_blksize); | ||
1808 | err |= __put_user(kbuf->blocks, &ubuf->st_blocks); | ||
1809 | return err; | ||
1810 | } | ||
1811 | |||
1812 | asmlinkage long | ||
1813 | sys32_stat64 (char __user *filename, struct stat64 __user *statbuf) | ||
1814 | { | ||
1815 | struct kstat s; | ||
1816 | long ret = vfs_stat(filename, &s); | ||
1817 | if (!ret) | ||
1818 | ret = putstat64(statbuf, &s); | ||
1819 | return ret; | ||
1820 | } | ||
1821 | |||
1822 | asmlinkage long | ||
1823 | sys32_lstat64 (char __user *filename, struct stat64 __user *statbuf) | ||
1824 | { | ||
1825 | struct kstat s; | ||
1826 | long ret = vfs_lstat(filename, &s); | ||
1827 | if (!ret) | ||
1828 | ret = putstat64(statbuf, &s); | ||
1829 | return ret; | ||
1830 | } | ||
1831 | |||
1832 | asmlinkage long | ||
1833 | sys32_fstat64 (unsigned int fd, struct stat64 __user *statbuf) | ||
1834 | { | ||
1835 | struct kstat s; | ||
1836 | long ret = vfs_fstat(fd, &s); | ||
1837 | if (!ret) | ||
1838 | ret = putstat64(statbuf, &s); | ||
1839 | return ret; | ||
1840 | } | ||
1841 | |||
1842 | asmlinkage long | ||
1843 | sys32_sched_rr_get_interval (pid_t pid, struct compat_timespec __user *interval) | ||
1844 | { | ||
1845 | mm_segment_t old_fs = get_fs(); | ||
1846 | struct timespec t; | ||
1847 | long ret; | ||
1848 | |||
1849 | set_fs(KERNEL_DS); | ||
1850 | ret = sys_sched_rr_get_interval(pid, (struct timespec __user *) &t); | ||
1851 | set_fs(old_fs); | ||
1852 | if (put_compat_timespec(&t, interval)) | ||
1853 | return -EFAULT; | ||
1854 | return ret; | ||
1855 | } | ||
1856 | |||
1857 | asmlinkage long | ||
1858 | sys32_pread (unsigned int fd, void __user *buf, unsigned int count, u32 pos_lo, u32 pos_hi) | ||
1859 | { | ||
1860 | return sys_pread64(fd, buf, count, ((unsigned long) pos_hi << 32) | pos_lo); | ||
1861 | } | ||
1862 | |||
1863 | asmlinkage long | ||
1864 | sys32_pwrite (unsigned int fd, void __user *buf, unsigned int count, u32 pos_lo, u32 pos_hi) | ||
1865 | { | ||
1866 | return sys_pwrite64(fd, buf, count, ((unsigned long) pos_hi << 32) | pos_lo); | ||
1867 | } | ||
1868 | |||
1869 | asmlinkage long | ||
1870 | sys32_sendfile (int out_fd, int in_fd, int __user *offset, unsigned int count) | ||
1871 | { | ||
1872 | mm_segment_t old_fs = get_fs(); | ||
1873 | long ret; | ||
1874 | off_t of; | ||
1875 | |||
1876 | if (offset && get_user(of, offset)) | ||
1877 | return -EFAULT; | ||
1878 | |||
1879 | set_fs(KERNEL_DS); | ||
1880 | ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *) &of : NULL, count); | ||
1881 | set_fs(old_fs); | ||
1882 | |||
1883 | if (offset && put_user(of, offset)) | ||
1884 | return -EFAULT; | ||
1885 | |||
1886 | return ret; | ||
1887 | } | ||
1888 | |||
1889 | asmlinkage long | ||
1890 | sys32_personality (unsigned int personality) | ||
1891 | { | ||
1892 | long ret; | ||
1893 | |||
1894 | if (current->personality == PER_LINUX32 && personality == PER_LINUX) | ||
1895 | personality = PER_LINUX32; | ||
1896 | ret = sys_personality(personality); | ||
1897 | if (ret == PER_LINUX32) | ||
1898 | ret = PER_LINUX; | ||
1899 | return ret; | ||
1900 | } | ||
1901 | |||
1902 | asmlinkage unsigned long | ||
1903 | sys32_brk (unsigned int brk) | ||
1904 | { | ||
1905 | unsigned long ret, obrk; | ||
1906 | struct mm_struct *mm = current->mm; | ||
1907 | |||
1908 | obrk = mm->brk; | ||
1909 | ret = sys_brk(brk); | ||
1910 | if (ret < obrk) | ||
1911 | clear_user(compat_ptr(ret), PAGE_ALIGN(ret) - ret); | ||
1912 | return ret; | ||
1913 | } | ||
1914 | |||
1915 | /* Structure for ia32 emulation on ia64 */ | ||
1916 | struct epoll_event32 | ||
1917 | { | ||
1918 | u32 events; | ||
1919 | u32 data[2]; | ||
1920 | }; | ||
1921 | |||
1922 | asmlinkage long | ||
1923 | sys32_epoll_ctl(int epfd, int op, int fd, struct epoll_event32 __user *event) | ||
1924 | { | ||
1925 | mm_segment_t old_fs = get_fs(); | ||
1926 | struct epoll_event event64; | ||
1927 | int error; | ||
1928 | u32 data_halfword; | ||
1929 | |||
1930 | if (!access_ok(VERIFY_READ, event, sizeof(struct epoll_event32))) | ||
1931 | return -EFAULT; | ||
1932 | |||
1933 | __get_user(event64.events, &event->events); | ||
1934 | __get_user(data_halfword, &event->data[0]); | ||
1935 | event64.data = data_halfword; | ||
1936 | __get_user(data_halfword, &event->data[1]); | ||
1937 | event64.data |= (u64)data_halfword << 32; | ||
1938 | |||
1939 | set_fs(KERNEL_DS); | ||
1940 | error = sys_epoll_ctl(epfd, op, fd, (struct epoll_event __user *) &event64); | ||
1941 | set_fs(old_fs); | ||
1942 | |||
1943 | return error; | ||
1944 | } | ||
1945 | |||
1946 | asmlinkage long | ||
1947 | sys32_epoll_wait(int epfd, struct epoll_event32 __user * events, int maxevents, | ||
1948 | int timeout) | ||
1949 | { | ||
1950 | struct epoll_event *events64 = NULL; | ||
1951 | mm_segment_t old_fs = get_fs(); | ||
1952 | int numevents, size; | ||
1953 | int evt_idx; | ||
1954 | int do_free_pages = 0; | ||
1955 | |||
1956 | if (maxevents <= 0) { | ||
1957 | return -EINVAL; | ||
1958 | } | ||
1959 | |||
1960 | /* Verify that the area passed by the user is writeable */ | ||
1961 | if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event32))) | ||
1962 | return -EFAULT; | ||
1963 | |||
1964 | /* | ||
1965 | * Allocate space for the intermediate copy. If the space needed | ||
1966 | * is large enough to cause kmalloc to fail, then try again with | ||
1967 | * __get_free_pages. | ||
1968 | */ | ||
1969 | size = maxevents * sizeof(struct epoll_event); | ||
1970 | events64 = kmalloc(size, GFP_KERNEL); | ||
1971 | if (events64 == NULL) { | ||
1972 | events64 = (struct epoll_event *) | ||
1973 | __get_free_pages(GFP_KERNEL, get_order(size)); | ||
1974 | if (events64 == NULL) | ||
1975 | return -ENOMEM; | ||
1976 | do_free_pages = 1; | ||
1977 | } | ||
1978 | |||
1979 | /* Do the system call */ | ||
1980 | set_fs(KERNEL_DS); /* copy_to/from_user should work on kernel mem*/ | ||
1981 | numevents = sys_epoll_wait(epfd, (struct epoll_event __user *) events64, | ||
1982 | maxevents, timeout); | ||
1983 | set_fs(old_fs); | ||
1984 | |||
1985 | /* Don't modify userspace memory if we're returning an error */ | ||
1986 | if (numevents > 0) { | ||
1987 | /* Translate the 64-bit structures back into the 32-bit | ||
1988 | structures */ | ||
1989 | for (evt_idx = 0; evt_idx < numevents; evt_idx++) { | ||
1990 | __put_user(events64[evt_idx].events, | ||
1991 | &events[evt_idx].events); | ||
1992 | __put_user((u32)events64[evt_idx].data, | ||
1993 | &events[evt_idx].data[0]); | ||
1994 | __put_user((u32)(events64[evt_idx].data >> 32), | ||
1995 | &events[evt_idx].data[1]); | ||
1996 | } | ||
1997 | } | ||
1998 | |||
1999 | if (do_free_pages) | ||
2000 | free_pages((unsigned long) events64, get_order(size)); | ||
2001 | else | ||
2002 | kfree(events64); | ||
2003 | return numevents; | ||
2004 | } | ||
2005 | |||
2006 | /* | ||
2007 | * Get a yet unused TLS descriptor index. | ||
2008 | */ | ||
2009 | static int | ||
2010 | get_free_idx (void) | ||
2011 | { | ||
2012 | struct thread_struct *t = ¤t->thread; | ||
2013 | int idx; | ||
2014 | |||
2015 | for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) | ||
2016 | if (desc_empty(t->tls_array + idx)) | ||
2017 | return idx + GDT_ENTRY_TLS_MIN; | ||
2018 | return -ESRCH; | ||
2019 | } | ||
2020 | |||
2021 | static void set_tls_desc(struct task_struct *p, int idx, | ||
2022 | const struct ia32_user_desc *info, int n) | ||
2023 | { | ||
2024 | struct thread_struct *t = &p->thread; | ||
2025 | struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; | ||
2026 | int cpu; | ||
2027 | |||
2028 | /* | ||
2029 | * We must not get preempted while modifying the TLS. | ||
2030 | */ | ||
2031 | cpu = get_cpu(); | ||
2032 | |||
2033 | while (n-- > 0) { | ||
2034 | if (LDT_empty(info)) { | ||
2035 | desc->a = 0; | ||
2036 | desc->b = 0; | ||
2037 | } else { | ||
2038 | desc->a = LDT_entry_a(info); | ||
2039 | desc->b = LDT_entry_b(info); | ||
2040 | } | ||
2041 | |||
2042 | ++info; | ||
2043 | ++desc; | ||
2044 | } | ||
2045 | |||
2046 | if (t == ¤t->thread) | ||
2047 | load_TLS(t, cpu); | ||
2048 | |||
2049 | put_cpu(); | ||
2050 | } | ||
2051 | |||
2052 | /* | ||
2053 | * Set a given TLS descriptor: | ||
2054 | */ | ||
2055 | asmlinkage int | ||
2056 | sys32_set_thread_area (struct ia32_user_desc __user *u_info) | ||
2057 | { | ||
2058 | struct ia32_user_desc info; | ||
2059 | int idx; | ||
2060 | |||
2061 | if (copy_from_user(&info, u_info, sizeof(info))) | ||
2062 | return -EFAULT; | ||
2063 | idx = info.entry_number; | ||
2064 | |||
2065 | /* | ||
2066 | * index -1 means the kernel should try to find and allocate an empty descriptor: | ||
2067 | */ | ||
2068 | if (idx == -1) { | ||
2069 | idx = get_free_idx(); | ||
2070 | if (idx < 0) | ||
2071 | return idx; | ||
2072 | if (put_user(idx, &u_info->entry_number)) | ||
2073 | return -EFAULT; | ||
2074 | } | ||
2075 | |||
2076 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
2077 | return -EINVAL; | ||
2078 | |||
2079 | set_tls_desc(current, idx, &info, 1); | ||
2080 | return 0; | ||
2081 | } | ||
2082 | |||
2083 | /* | ||
2084 | * Get the current Thread-Local Storage area: | ||
2085 | */ | ||
2086 | |||
2087 | #define GET_BASE(desc) ( \ | ||
2088 | (((desc)->a >> 16) & 0x0000ffff) | \ | ||
2089 | (((desc)->b << 16) & 0x00ff0000) | \ | ||
2090 | ( (desc)->b & 0xff000000) ) | ||
2091 | |||
2092 | #define GET_LIMIT(desc) ( \ | ||
2093 | ((desc)->a & 0x0ffff) | \ | ||
2094 | ((desc)->b & 0xf0000) ) | ||
2095 | |||
2096 | #define GET_32BIT(desc) (((desc)->b >> 22) & 1) | ||
2097 | #define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) | ||
2098 | #define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) | ||
2099 | #define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) | ||
2100 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) | ||
2101 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) | ||
2102 | |||
2103 | static void fill_user_desc(struct ia32_user_desc *info, int idx, | ||
2104 | const struct desc_struct *desc) | ||
2105 | { | ||
2106 | info->entry_number = idx; | ||
2107 | info->base_addr = GET_BASE(desc); | ||
2108 | info->limit = GET_LIMIT(desc); | ||
2109 | info->seg_32bit = GET_32BIT(desc); | ||
2110 | info->contents = GET_CONTENTS(desc); | ||
2111 | info->read_exec_only = !GET_WRITABLE(desc); | ||
2112 | info->limit_in_pages = GET_LIMIT_PAGES(desc); | ||
2113 | info->seg_not_present = !GET_PRESENT(desc); | ||
2114 | info->useable = GET_USEABLE(desc); | ||
2115 | } | ||
2116 | |||
2117 | asmlinkage int | ||
2118 | sys32_get_thread_area (struct ia32_user_desc __user *u_info) | ||
2119 | { | ||
2120 | struct ia32_user_desc info; | ||
2121 | struct desc_struct *desc; | ||
2122 | int idx; | ||
2123 | |||
2124 | if (get_user(idx, &u_info->entry_number)) | ||
2125 | return -EFAULT; | ||
2126 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
2127 | return -EINVAL; | ||
2128 | |||
2129 | desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; | ||
2130 | fill_user_desc(&info, idx, desc); | ||
2131 | |||
2132 | if (copy_to_user(u_info, &info, sizeof(info))) | ||
2133 | return -EFAULT; | ||
2134 | return 0; | ||
2135 | } | ||
2136 | |||
2137 | struct regset_get { | ||
2138 | void *kbuf; | ||
2139 | void __user *ubuf; | ||
2140 | }; | ||
2141 | |||
2142 | struct regset_set { | ||
2143 | const void *kbuf; | ||
2144 | const void __user *ubuf; | ||
2145 | }; | ||
2146 | |||
2147 | struct regset_getset { | ||
2148 | struct task_struct *target; | ||
2149 | const struct user_regset *regset; | ||
2150 | union { | ||
2151 | struct regset_get get; | ||
2152 | struct regset_set set; | ||
2153 | } u; | ||
2154 | unsigned int pos; | ||
2155 | unsigned int count; | ||
2156 | int ret; | ||
2157 | }; | ||
2158 | |||
2159 | static void getfpreg(struct task_struct *task, int regno, int *val) | ||
2160 | { | ||
2161 | switch (regno / sizeof(int)) { | ||
2162 | case 0: | ||
2163 | *val = task->thread.fcr & 0xffff; | ||
2164 | break; | ||
2165 | case 1: | ||
2166 | *val = task->thread.fsr & 0xffff; | ||
2167 | break; | ||
2168 | case 2: | ||
2169 | *val = (task->thread.fsr>>16) & 0xffff; | ||
2170 | break; | ||
2171 | case 3: | ||
2172 | *val = task->thread.fir; | ||
2173 | break; | ||
2174 | case 4: | ||
2175 | *val = (task->thread.fir>>32) & 0xffff; | ||
2176 | break; | ||
2177 | case 5: | ||
2178 | *val = task->thread.fdr; | ||
2179 | break; | ||
2180 | case 6: | ||
2181 | *val = (task->thread.fdr >> 32) & 0xffff; | ||
2182 | break; | ||
2183 | } | ||
2184 | } | ||
2185 | |||
2186 | static void setfpreg(struct task_struct *task, int regno, int val) | ||
2187 | { | ||
2188 | switch (regno / sizeof(int)) { | ||
2189 | case 0: | ||
2190 | task->thread.fcr = (task->thread.fcr & (~0x1f3f)) | ||
2191 | | (val & 0x1f3f); | ||
2192 | break; | ||
2193 | case 1: | ||
2194 | task->thread.fsr = (task->thread.fsr & (~0xffff)) | val; | ||
2195 | break; | ||
2196 | case 2: | ||
2197 | task->thread.fsr = (task->thread.fsr & (~0xffff0000)) | ||
2198 | | (val << 16); | ||
2199 | break; | ||
2200 | case 3: | ||
2201 | task->thread.fir = (task->thread.fir & (~0xffffffff)) | val; | ||
2202 | break; | ||
2203 | case 5: | ||
2204 | task->thread.fdr = (task->thread.fdr & (~0xffffffff)) | val; | ||
2205 | break; | ||
2206 | } | ||
2207 | } | ||
2208 | |||
2209 | static void access_fpreg_ia32(int regno, void *reg, | ||
2210 | struct pt_regs *pt, struct switch_stack *sw, | ||
2211 | int tos, int write) | ||
2212 | { | ||
2213 | void *f; | ||
2214 | |||
2215 | if ((regno += tos) >= 8) | ||
2216 | regno -= 8; | ||
2217 | if (regno < 4) | ||
2218 | f = &pt->f8 + regno; | ||
2219 | else if (regno <= 7) | ||
2220 | f = &sw->f12 + (regno - 4); | ||
2221 | else { | ||
2222 | printk(KERN_ERR "regno must be less than 7 \n"); | ||
2223 | return; | ||
2224 | } | ||
2225 | |||
2226 | if (write) | ||
2227 | memcpy(f, reg, sizeof(struct _fpreg_ia32)); | ||
2228 | else | ||
2229 | memcpy(reg, f, sizeof(struct _fpreg_ia32)); | ||
2230 | } | ||
2231 | |||
2232 | static void do_fpregs_get(struct unw_frame_info *info, void *arg) | ||
2233 | { | ||
2234 | struct regset_getset *dst = arg; | ||
2235 | struct task_struct *task = dst->target; | ||
2236 | struct pt_regs *pt; | ||
2237 | int start, end, tos; | ||
2238 | char buf[80]; | ||
2239 | |||
2240 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
2241 | return; | ||
2242 | if (dst->pos < 7 * sizeof(int)) { | ||
2243 | end = min((dst->pos + dst->count), | ||
2244 | (unsigned int)(7 * sizeof(int))); | ||
2245 | for (start = dst->pos; start < end; start += sizeof(int)) | ||
2246 | getfpreg(task, start, (int *)(buf + start)); | ||
2247 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
2248 | &dst->u.get.kbuf, &dst->u.get.ubuf, buf, | ||
2249 | 0, 7 * sizeof(int)); | ||
2250 | if (dst->ret || dst->count == 0) | ||
2251 | return; | ||
2252 | } | ||
2253 | if (dst->pos < sizeof(struct ia32_user_i387_struct)) { | ||
2254 | pt = task_pt_regs(task); | ||
2255 | tos = (task->thread.fsr >> 11) & 7; | ||
2256 | end = min(dst->pos + dst->count, | ||
2257 | (unsigned int)(sizeof(struct ia32_user_i387_struct))); | ||
2258 | start = (dst->pos - 7 * sizeof(int)) / | ||
2259 | sizeof(struct _fpreg_ia32); | ||
2260 | end = (end - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32); | ||
2261 | for (; start < end; start++) | ||
2262 | access_fpreg_ia32(start, | ||
2263 | (struct _fpreg_ia32 *)buf + start, | ||
2264 | pt, info->sw, tos, 0); | ||
2265 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
2266 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
2267 | buf, 7 * sizeof(int), | ||
2268 | sizeof(struct ia32_user_i387_struct)); | ||
2269 | if (dst->ret || dst->count == 0) | ||
2270 | return; | ||
2271 | } | ||
2272 | } | ||
2273 | |||
2274 | static void do_fpregs_set(struct unw_frame_info *info, void *arg) | ||
2275 | { | ||
2276 | struct regset_getset *dst = arg; | ||
2277 | struct task_struct *task = dst->target; | ||
2278 | struct pt_regs *pt; | ||
2279 | char buf[80]; | ||
2280 | int end, start, tos; | ||
2281 | |||
2282 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
2283 | return; | ||
2284 | |||
2285 | if (dst->pos < 7 * sizeof(int)) { | ||
2286 | start = dst->pos; | ||
2287 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
2288 | &dst->u.set.kbuf, &dst->u.set.ubuf, buf, | ||
2289 | 0, 7 * sizeof(int)); | ||
2290 | if (dst->ret) | ||
2291 | return; | ||
2292 | for (; start < dst->pos; start += sizeof(int)) | ||
2293 | setfpreg(task, start, *((int *)(buf + start))); | ||
2294 | if (dst->count == 0) | ||
2295 | return; | ||
2296 | } | ||
2297 | if (dst->pos < sizeof(struct ia32_user_i387_struct)) { | ||
2298 | start = (dst->pos - 7 * sizeof(int)) / | ||
2299 | sizeof(struct _fpreg_ia32); | ||
2300 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
2301 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
2302 | buf, 7 * sizeof(int), | ||
2303 | sizeof(struct ia32_user_i387_struct)); | ||
2304 | if (dst->ret) | ||
2305 | return; | ||
2306 | pt = task_pt_regs(task); | ||
2307 | tos = (task->thread.fsr >> 11) & 7; | ||
2308 | end = (dst->pos - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32); | ||
2309 | for (; start < end; start++) | ||
2310 | access_fpreg_ia32(start, | ||
2311 | (struct _fpreg_ia32 *)buf + start, | ||
2312 | pt, info->sw, tos, 1); | ||
2313 | if (dst->count == 0) | ||
2314 | return; | ||
2315 | } | ||
2316 | } | ||
2317 | |||
2318 | #define OFFSET(member) ((int)(offsetof(struct ia32_user_fxsr_struct, member))) | ||
2319 | static void getfpxreg(struct task_struct *task, int start, int end, char *buf) | ||
2320 | { | ||
2321 | int min_val; | ||
2322 | |||
2323 | min_val = min(end, OFFSET(fop)); | ||
2324 | while (start < min_val) { | ||
2325 | if (start == OFFSET(cwd)) | ||
2326 | *((short *)buf) = task->thread.fcr & 0xffff; | ||
2327 | else if (start == OFFSET(swd)) | ||
2328 | *((short *)buf) = task->thread.fsr & 0xffff; | ||
2329 | else if (start == OFFSET(twd)) | ||
2330 | *((short *)buf) = (task->thread.fsr>>16) & 0xffff; | ||
2331 | buf += 2; | ||
2332 | start += 2; | ||
2333 | } | ||
2334 | /* skip fop element */ | ||
2335 | if (start == OFFSET(fop)) { | ||
2336 | start += 2; | ||
2337 | buf += 2; | ||
2338 | } | ||
2339 | while (start < end) { | ||
2340 | if (start == OFFSET(fip)) | ||
2341 | *((int *)buf) = task->thread.fir; | ||
2342 | else if (start == OFFSET(fcs)) | ||
2343 | *((int *)buf) = (task->thread.fir>>32) & 0xffff; | ||
2344 | else if (start == OFFSET(foo)) | ||
2345 | *((int *)buf) = task->thread.fdr; | ||
2346 | else if (start == OFFSET(fos)) | ||
2347 | *((int *)buf) = (task->thread.fdr>>32) & 0xffff; | ||
2348 | else if (start == OFFSET(mxcsr)) | ||
2349 | *((int *)buf) = ((task->thread.fcr>>32) & 0xff80) | ||
2350 | | ((task->thread.fsr>>32) & 0x3f); | ||
2351 | buf += 4; | ||
2352 | start += 4; | ||
2353 | } | ||
2354 | } | ||
2355 | |||
2356 | static void setfpxreg(struct task_struct *task, int start, int end, char *buf) | ||
2357 | { | ||
2358 | int min_val, num32; | ||
2359 | short num; | ||
2360 | unsigned long num64; | ||
2361 | |||
2362 | min_val = min(end, OFFSET(fop)); | ||
2363 | while (start < min_val) { | ||
2364 | num = *((short *)buf); | ||
2365 | if (start == OFFSET(cwd)) { | ||
2366 | task->thread.fcr = (task->thread.fcr & (~0x1f3f)) | ||
2367 | | (num & 0x1f3f); | ||
2368 | } else if (start == OFFSET(swd)) { | ||
2369 | task->thread.fsr = (task->thread.fsr & (~0xffff)) | num; | ||
2370 | } else if (start == OFFSET(twd)) { | ||
2371 | task->thread.fsr = (task->thread.fsr & (~0xffff0000)) | ||
2372 | | (((int)num) << 16); | ||
2373 | } | ||
2374 | buf += 2; | ||
2375 | start += 2; | ||
2376 | } | ||
2377 | /* skip fop element */ | ||
2378 | if (start == OFFSET(fop)) { | ||
2379 | start += 2; | ||
2380 | buf += 2; | ||
2381 | } | ||
2382 | while (start < end) { | ||
2383 | num32 = *((int *)buf); | ||
2384 | if (start == OFFSET(fip)) | ||
2385 | task->thread.fir = (task->thread.fir & (~0xffffffff)) | ||
2386 | | num32; | ||
2387 | else if (start == OFFSET(foo)) | ||
2388 | task->thread.fdr = (task->thread.fdr & (~0xffffffff)) | ||
2389 | | num32; | ||
2390 | else if (start == OFFSET(mxcsr)) { | ||
2391 | num64 = num32 & 0xff10; | ||
2392 | task->thread.fcr = (task->thread.fcr & | ||
2393 | (~0xff1000000000UL)) | (num64<<32); | ||
2394 | num64 = num32 & 0x3f; | ||
2395 | task->thread.fsr = (task->thread.fsr & | ||
2396 | (~0x3f00000000UL)) | (num64<<32); | ||
2397 | } | ||
2398 | buf += 4; | ||
2399 | start += 4; | ||
2400 | } | ||
2401 | } | ||
2402 | |||
2403 | static void do_fpxregs_get(struct unw_frame_info *info, void *arg) | ||
2404 | { | ||
2405 | struct regset_getset *dst = arg; | ||
2406 | struct task_struct *task = dst->target; | ||
2407 | struct pt_regs *pt; | ||
2408 | char buf[128]; | ||
2409 | int start, end, tos; | ||
2410 | |||
2411 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
2412 | return; | ||
2413 | if (dst->pos < OFFSET(st_space[0])) { | ||
2414 | end = min(dst->pos + dst->count, (unsigned int)32); | ||
2415 | getfpxreg(task, dst->pos, end, buf); | ||
2416 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
2417 | &dst->u.get.kbuf, &dst->u.get.ubuf, buf, | ||
2418 | 0, OFFSET(st_space[0])); | ||
2419 | if (dst->ret || dst->count == 0) | ||
2420 | return; | ||
2421 | } | ||
2422 | if (dst->pos < OFFSET(xmm_space[0])) { | ||
2423 | pt = task_pt_regs(task); | ||
2424 | tos = (task->thread.fsr >> 11) & 7; | ||
2425 | end = min(dst->pos + dst->count, | ||
2426 | (unsigned int)OFFSET(xmm_space[0])); | ||
2427 | start = (dst->pos - OFFSET(st_space[0])) / 16; | ||
2428 | end = (end - OFFSET(st_space[0])) / 16; | ||
2429 | for (; start < end; start++) | ||
2430 | access_fpreg_ia32(start, buf + 16 * start, pt, | ||
2431 | info->sw, tos, 0); | ||
2432 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
2433 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
2434 | buf, OFFSET(st_space[0]), OFFSET(xmm_space[0])); | ||
2435 | if (dst->ret || dst->count == 0) | ||
2436 | return; | ||
2437 | } | ||
2438 | if (dst->pos < OFFSET(padding[0])) | ||
2439 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
2440 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
2441 | &info->sw->f16, OFFSET(xmm_space[0]), | ||
2442 | OFFSET(padding[0])); | ||
2443 | } | ||
2444 | |||
2445 | static void do_fpxregs_set(struct unw_frame_info *info, void *arg) | ||
2446 | { | ||
2447 | struct regset_getset *dst = arg; | ||
2448 | struct task_struct *task = dst->target; | ||
2449 | char buf[128]; | ||
2450 | int start, end; | ||
2451 | |||
2452 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
2453 | return; | ||
2454 | |||
2455 | if (dst->pos < OFFSET(st_space[0])) { | ||
2456 | start = dst->pos; | ||
2457 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
2458 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
2459 | buf, 0, OFFSET(st_space[0])); | ||
2460 | if (dst->ret) | ||
2461 | return; | ||
2462 | setfpxreg(task, start, dst->pos, buf); | ||
2463 | if (dst->count == 0) | ||
2464 | return; | ||
2465 | } | ||
2466 | if (dst->pos < OFFSET(xmm_space[0])) { | ||
2467 | struct pt_regs *pt; | ||
2468 | int tos; | ||
2469 | pt = task_pt_regs(task); | ||
2470 | tos = (task->thread.fsr >> 11) & 7; | ||
2471 | start = (dst->pos - OFFSET(st_space[0])) / 16; | ||
2472 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
2473 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
2474 | buf, OFFSET(st_space[0]), OFFSET(xmm_space[0])); | ||
2475 | if (dst->ret) | ||
2476 | return; | ||
2477 | end = (dst->pos - OFFSET(st_space[0])) / 16; | ||
2478 | for (; start < end; start++) | ||
2479 | access_fpreg_ia32(start, buf + 16 * start, pt, info->sw, | ||
2480 | tos, 1); | ||
2481 | if (dst->count == 0) | ||
2482 | return; | ||
2483 | } | ||
2484 | if (dst->pos < OFFSET(padding[0])) | ||
2485 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
2486 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
2487 | &info->sw->f16, OFFSET(xmm_space[0]), | ||
2488 | OFFSET(padding[0])); | ||
2489 | } | ||
2490 | #undef OFFSET | ||
2491 | |||
2492 | static int do_regset_call(void (*call)(struct unw_frame_info *, void *), | ||
2493 | struct task_struct *target, | ||
2494 | const struct user_regset *regset, | ||
2495 | unsigned int pos, unsigned int count, | ||
2496 | const void *kbuf, const void __user *ubuf) | ||
2497 | { | ||
2498 | struct regset_getset info = { .target = target, .regset = regset, | ||
2499 | .pos = pos, .count = count, | ||
2500 | .u.set = { .kbuf = kbuf, .ubuf = ubuf }, | ||
2501 | .ret = 0 }; | ||
2502 | |||
2503 | if (target == current) | ||
2504 | unw_init_running(call, &info); | ||
2505 | else { | ||
2506 | struct unw_frame_info ufi; | ||
2507 | memset(&ufi, 0, sizeof(ufi)); | ||
2508 | unw_init_from_blocked_task(&ufi, target); | ||
2509 | (*call)(&ufi, &info); | ||
2510 | } | ||
2511 | |||
2512 | return info.ret; | ||
2513 | } | ||
2514 | |||
2515 | static int ia32_fpregs_get(struct task_struct *target, | ||
2516 | const struct user_regset *regset, | ||
2517 | unsigned int pos, unsigned int count, | ||
2518 | void *kbuf, void __user *ubuf) | ||
2519 | { | ||
2520 | return do_regset_call(do_fpregs_get, target, regset, pos, count, | ||
2521 | kbuf, ubuf); | ||
2522 | } | ||
2523 | |||
2524 | static int ia32_fpregs_set(struct task_struct *target, | ||
2525 | const struct user_regset *regset, | ||
2526 | unsigned int pos, unsigned int count, | ||
2527 | const void *kbuf, const void __user *ubuf) | ||
2528 | { | ||
2529 | return do_regset_call(do_fpregs_set, target, regset, pos, count, | ||
2530 | kbuf, ubuf); | ||
2531 | } | ||
2532 | |||
2533 | static int ia32_fpxregs_get(struct task_struct *target, | ||
2534 | const struct user_regset *regset, | ||
2535 | unsigned int pos, unsigned int count, | ||
2536 | void *kbuf, void __user *ubuf) | ||
2537 | { | ||
2538 | return do_regset_call(do_fpxregs_get, target, regset, pos, count, | ||
2539 | kbuf, ubuf); | ||
2540 | } | ||
2541 | |||
2542 | static int ia32_fpxregs_set(struct task_struct *target, | ||
2543 | const struct user_regset *regset, | ||
2544 | unsigned int pos, unsigned int count, | ||
2545 | const void *kbuf, const void __user *ubuf) | ||
2546 | { | ||
2547 | return do_regset_call(do_fpxregs_set, target, regset, pos, count, | ||
2548 | kbuf, ubuf); | ||
2549 | } | ||
2550 | |||
2551 | static int ia32_genregs_get(struct task_struct *target, | ||
2552 | const struct user_regset *regset, | ||
2553 | unsigned int pos, unsigned int count, | ||
2554 | void *kbuf, void __user *ubuf) | ||
2555 | { | ||
2556 | if (kbuf) { | ||
2557 | u32 *kp = kbuf; | ||
2558 | while (count > 0) { | ||
2559 | *kp++ = getreg(target, pos); | ||
2560 | pos += 4; | ||
2561 | count -= 4; | ||
2562 | } | ||
2563 | } else { | ||
2564 | u32 __user *up = ubuf; | ||
2565 | while (count > 0) { | ||
2566 | if (__put_user(getreg(target, pos), up++)) | ||
2567 | return -EFAULT; | ||
2568 | pos += 4; | ||
2569 | count -= 4; | ||
2570 | } | ||
2571 | } | ||
2572 | return 0; | ||
2573 | } | ||
2574 | |||
2575 | static int ia32_genregs_set(struct task_struct *target, | ||
2576 | const struct user_regset *regset, | ||
2577 | unsigned int pos, unsigned int count, | ||
2578 | const void *kbuf, const void __user *ubuf) | ||
2579 | { | ||
2580 | int ret = 0; | ||
2581 | |||
2582 | if (kbuf) { | ||
2583 | const u32 *kp = kbuf; | ||
2584 | while (!ret && count > 0) { | ||
2585 | putreg(target, pos, *kp++); | ||
2586 | pos += 4; | ||
2587 | count -= 4; | ||
2588 | } | ||
2589 | } else { | ||
2590 | const u32 __user *up = ubuf; | ||
2591 | u32 val; | ||
2592 | while (!ret && count > 0) { | ||
2593 | ret = __get_user(val, up++); | ||
2594 | if (!ret) | ||
2595 | putreg(target, pos, val); | ||
2596 | pos += 4; | ||
2597 | count -= 4; | ||
2598 | } | ||
2599 | } | ||
2600 | return ret; | ||
2601 | } | ||
2602 | |||
2603 | static int ia32_tls_active(struct task_struct *target, | ||
2604 | const struct user_regset *regset) | ||
2605 | { | ||
2606 | struct thread_struct *t = &target->thread; | ||
2607 | int n = GDT_ENTRY_TLS_ENTRIES; | ||
2608 | while (n > 0 && desc_empty(&t->tls_array[n -1])) | ||
2609 | --n; | ||
2610 | return n; | ||
2611 | } | ||
2612 | |||
2613 | static int ia32_tls_get(struct task_struct *target, | ||
2614 | const struct user_regset *regset, unsigned int pos, | ||
2615 | unsigned int count, void *kbuf, void __user *ubuf) | ||
2616 | { | ||
2617 | const struct desc_struct *tls; | ||
2618 | |||
2619 | if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct ia32_user_desc) || | ||
2620 | (pos % sizeof(struct ia32_user_desc)) != 0 || | ||
2621 | (count % sizeof(struct ia32_user_desc)) != 0) | ||
2622 | return -EINVAL; | ||
2623 | |||
2624 | pos /= sizeof(struct ia32_user_desc); | ||
2625 | count /= sizeof(struct ia32_user_desc); | ||
2626 | |||
2627 | tls = &target->thread.tls_array[pos]; | ||
2628 | |||
2629 | if (kbuf) { | ||
2630 | struct ia32_user_desc *info = kbuf; | ||
2631 | while (count-- > 0) | ||
2632 | fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++, | ||
2633 | tls++); | ||
2634 | } else { | ||
2635 | struct ia32_user_desc __user *u_info = ubuf; | ||
2636 | while (count-- > 0) { | ||
2637 | struct ia32_user_desc info; | ||
2638 | fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++); | ||
2639 | if (__copy_to_user(u_info++, &info, sizeof(info))) | ||
2640 | return -EFAULT; | ||
2641 | } | ||
2642 | } | ||
2643 | |||
2644 | return 0; | ||
2645 | } | ||
2646 | |||
2647 | static int ia32_tls_set(struct task_struct *target, | ||
2648 | const struct user_regset *regset, unsigned int pos, | ||
2649 | unsigned int count, const void *kbuf, const void __user *ubuf) | ||
2650 | { | ||
2651 | struct ia32_user_desc infobuf[GDT_ENTRY_TLS_ENTRIES]; | ||
2652 | const struct ia32_user_desc *info; | ||
2653 | |||
2654 | if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct ia32_user_desc) || | ||
2655 | (pos % sizeof(struct ia32_user_desc)) != 0 || | ||
2656 | (count % sizeof(struct ia32_user_desc)) != 0) | ||
2657 | return -EINVAL; | ||
2658 | |||
2659 | if (kbuf) | ||
2660 | info = kbuf; | ||
2661 | else if (__copy_from_user(infobuf, ubuf, count)) | ||
2662 | return -EFAULT; | ||
2663 | else | ||
2664 | info = infobuf; | ||
2665 | |||
2666 | set_tls_desc(target, | ||
2667 | GDT_ENTRY_TLS_MIN + (pos / sizeof(struct ia32_user_desc)), | ||
2668 | info, count / sizeof(struct ia32_user_desc)); | ||
2669 | |||
2670 | return 0; | ||
2671 | } | ||
2672 | |||
2673 | /* | ||
2674 | * This should match arch/i386/kernel/ptrace.c:native_regsets. | ||
2675 | * XXX ioperm? vm86? | ||
2676 | */ | ||
2677 | static const struct user_regset ia32_regsets[] = { | ||
2678 | { | ||
2679 | .core_note_type = NT_PRSTATUS, | ||
2680 | .n = sizeof(struct user_regs_struct32)/4, | ||
2681 | .size = 4, .align = 4, | ||
2682 | .get = ia32_genregs_get, .set = ia32_genregs_set | ||
2683 | }, | ||
2684 | { | ||
2685 | .core_note_type = NT_PRFPREG, | ||
2686 | .n = sizeof(struct ia32_user_i387_struct) / 4, | ||
2687 | .size = 4, .align = 4, | ||
2688 | .get = ia32_fpregs_get, .set = ia32_fpregs_set | ||
2689 | }, | ||
2690 | { | ||
2691 | .core_note_type = NT_PRXFPREG, | ||
2692 | .n = sizeof(struct ia32_user_fxsr_struct) / 4, | ||
2693 | .size = 4, .align = 4, | ||
2694 | .get = ia32_fpxregs_get, .set = ia32_fpxregs_set | ||
2695 | }, | ||
2696 | { | ||
2697 | .core_note_type = NT_386_TLS, | ||
2698 | .n = GDT_ENTRY_TLS_ENTRIES, | ||
2699 | .bias = GDT_ENTRY_TLS_MIN, | ||
2700 | .size = sizeof(struct ia32_user_desc), | ||
2701 | .align = sizeof(struct ia32_user_desc), | ||
2702 | .active = ia32_tls_active, | ||
2703 | .get = ia32_tls_get, .set = ia32_tls_set, | ||
2704 | }, | ||
2705 | }; | ||
2706 | |||
2707 | const struct user_regset_view user_ia32_view = { | ||
2708 | .name = "i386", .e_machine = EM_386, | ||
2709 | .regsets = ia32_regsets, .n = ARRAY_SIZE(ia32_regsets) | ||
2710 | }; | ||
2711 | |||
2712 | long sys32_fadvise64_64(int fd, __u32 offset_low, __u32 offset_high, | ||
2713 | __u32 len_low, __u32 len_high, int advice) | ||
2714 | { | ||
2715 | return sys_fadvise64_64(fd, | ||
2716 | (((u64)offset_high)<<32) | offset_low, | ||
2717 | (((u64)len_high)<<32) | len_low, | ||
2718 | advice); | ||
2719 | } | ||
2720 | |||
2721 | #ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */ | ||
2722 | |||
2723 | asmlinkage long sys32_setreuid(compat_uid_t ruid, compat_uid_t euid) | ||
2724 | { | ||
2725 | uid_t sruid, seuid; | ||
2726 | |||
2727 | sruid = (ruid == (compat_uid_t)-1) ? ((uid_t)-1) : ((uid_t)ruid); | ||
2728 | seuid = (euid == (compat_uid_t)-1) ? ((uid_t)-1) : ((uid_t)euid); | ||
2729 | return sys_setreuid(sruid, seuid); | ||
2730 | } | ||
2731 | |||
2732 | asmlinkage long | ||
2733 | sys32_setresuid(compat_uid_t ruid, compat_uid_t euid, | ||
2734 | compat_uid_t suid) | ||
2735 | { | ||
2736 | uid_t sruid, seuid, ssuid; | ||
2737 | |||
2738 | sruid = (ruid == (compat_uid_t)-1) ? ((uid_t)-1) : ((uid_t)ruid); | ||
2739 | seuid = (euid == (compat_uid_t)-1) ? ((uid_t)-1) : ((uid_t)euid); | ||
2740 | ssuid = (suid == (compat_uid_t)-1) ? ((uid_t)-1) : ((uid_t)suid); | ||
2741 | return sys_setresuid(sruid, seuid, ssuid); | ||
2742 | } | ||
2743 | |||
2744 | asmlinkage long | ||
2745 | sys32_setregid(compat_gid_t rgid, compat_gid_t egid) | ||
2746 | { | ||
2747 | gid_t srgid, segid; | ||
2748 | |||
2749 | srgid = (rgid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)rgid); | ||
2750 | segid = (egid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)egid); | ||
2751 | return sys_setregid(srgid, segid); | ||
2752 | } | ||
2753 | |||
2754 | asmlinkage long | ||
2755 | sys32_setresgid(compat_gid_t rgid, compat_gid_t egid, | ||
2756 | compat_gid_t sgid) | ||
2757 | { | ||
2758 | gid_t srgid, segid, ssgid; | ||
2759 | |||
2760 | srgid = (rgid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)rgid); | ||
2761 | segid = (egid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)egid); | ||
2762 | ssgid = (sgid == (compat_gid_t)-1) ? ((gid_t)-1) : ((gid_t)sgid); | ||
2763 | return sys_setresgid(srgid, segid, ssgid); | ||
2764 | } | ||
2765 | #endif /* NOTYET */ | ||