File: /usr/src/linux/arch/ia64/ia32/ia32_ldt.c
1 /*
2 * Copyright (C) 2001 Hewlett-Packard Co
3 * Copyright (C) 2001 David Mosberger-Tang <davidm@hpl.hp.com>
4 *
5 * Adapted from arch/i386/kernel/ldt.c
6 */
7
8 #include <linux/errno.h>
9 #include <linux/sched.h>
10 #include <linux/string.h>
11 #include <linux/mm.h>
12 #include <linux/smp.h>
13 #include <linux/smp_lock.h>
14 #include <linux/vmalloc.h>
15
16 #include <asm/uaccess.h>
17 #include <asm/ia32.h>
18
19 /*
20 * read_ldt() is not really atomic - this is not a problem since synchronization of reads
21 * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic,
22 * to protect the security checks done on new descriptors.
23 */
24 static int
25 read_ldt (void *ptr, unsigned long bytecount)
26 {
27 char *src, *dst, buf[256]; /* temporary buffer (don't overflow kernel stack!) */
28 unsigned long bytes_left, n;
29
30 if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE)
31 bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE;
32
33 bytes_left = bytecount;
34
35 src = (void *) IA32_LDT_OFFSET;
36 dst = ptr;
37
38 while (bytes_left) {
39 n = sizeof(buf);
40 if (n > bytes_left)
41 n = bytes_left;
42
43 /*
44 * We know we're reading valid memory, but we still must guard against
45 * running out of memory.
46 */
47 if (__copy_from_user(buf, src, n))
48 return -EFAULT;
49
50 if (copy_to_user(dst, buf, n))
51 return -EFAULT;
52
53 src += n;
54 dst += n;
55 bytes_left -= n;
56 }
57 return bytecount;
58 }
59
60 static int
61 write_ldt (void * ptr, unsigned long bytecount, int oldmode)
62 {
63 struct ia32_modify_ldt_ldt_s ldt_info;
64 __u64 entry;
65
66 if (bytecount != sizeof(ldt_info))
67 return -EINVAL;
68 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
69 return -EFAULT;
70
71 if (ldt_info.entry_number >= IA32_LDT_ENTRIES)
72 return -EINVAL;
73 if (ldt_info.contents == 3) {
74 if (oldmode)
75 return -EINVAL;
76 if (ldt_info.seg_not_present == 0)
77 return -EINVAL;
78 }
79
80 if (ldt_info.base_addr == 0 && ldt_info.limit == 0
81 && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1
82 && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0
83 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0)))
84 /* allow LDTs to be cleared by the user */
85 entry = 0;
86 else
87 /* we must set the "Accessed" bit as IVE doesn't emulate it */
88 entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit,
89 (((ldt_info.read_exec_only ^ 1) << 1)
90 | (ldt_info.contents << 2)) | 1,
91 1, 3, ldt_info.seg_not_present ^ 1,
92 (oldmode ? 0 : ldt_info.useable),
93 ldt_info.seg_32bit,
94 ldt_info.limit_in_pages);
95 /*
96 * Install the new entry. We know we're accessing valid (mapped) user-level
97 * memory, but we still need to guard against out-of-memory, hence we must use
98 * put_user().
99 */
100 return __put_user(entry, (__u64 *) IA32_LDT_OFFSET + ldt_info.entry_number);
101 }
102
103 asmlinkage int
104 sys32_modify_ldt (int func, void *ptr, unsigned int bytecount)
105 {
106 int ret = -ENOSYS;
107
108 switch (func) {
109 case 0:
110 ret = read_ldt(ptr, bytecount);
111 break;
112 case 1:
113 ret = write_ldt(ptr, bytecount, 1);
114 break;
115 case 0x11:
116 ret = write_ldt(ptr, bytecount, 0);
117 break;
118 }
119 return ret;
120 }
121