1
2
3
4
5
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/system.h>
18#include <asm/ldt.h>
19#include <asm/desc.h>
20
21
22
23
24
25
26
27static int read_ldt(void * ptr, unsigned long bytecount)
28{
29 int err;
30 unsigned long size;
31 struct mm_struct * mm = current->mm;
32
33 err = 0;
34 if (!mm->context.segments)
35 goto out;
36
37 size = LDT_ENTRIES*LDT_ENTRY_SIZE;
38 if (size > bytecount)
39 size = bytecount;
40
41 err = size;
42 if (copy_to_user(ptr, mm->context.segments, size))
43 err = -EFAULT;
44out:
45 return err;
46}
47
48static int read_default_ldt(void * ptr, unsigned long bytecount)
49{
50 int err;
51 unsigned long size;
52 void *address;
53
54 err = 0;
55 address = &default_ldt[0];
56 size = sizeof(struct desc_struct);
57 if (size > bytecount)
58 size = bytecount;
59
60 err = size;
61 if (copy_to_user(ptr, address, size))
62 err = -EFAULT;
63
64 return err;
65}
66
67static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
68{
69 struct mm_struct * mm = current->mm;
70 __u32 entry_1, entry_2, *lp;
71 int error;
72 struct modify_ldt_ldt_s ldt_info;
73
74 error = -EINVAL;
75 if (bytecount != sizeof(ldt_info))
76 goto out;
77 error = -EFAULT;
78 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
79 goto out;
80
81 error = -EINVAL;
82 if (ldt_info.entry_number >= LDT_ENTRIES)
83 goto out;
84 if (ldt_info.contents == 3) {
85 if (oldmode)
86 goto out;
87 if (ldt_info.seg_not_present == 0)
88 goto out;
89 }
90
91
92
93
94
95 down_write(&mm->mmap_sem);
96 if (!mm->context.segments) {
97 void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
98 error = -ENOMEM;
99 if (!segments)
100 goto out_unlock;
101 memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
102 wmb();
103 mm->context.segments = segments;
104 mm->context.cpuvalid = 1UL << smp_processor_id();
105 load_LDT(mm);
106 }
107
108 lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments);
109
110
111 if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
112 if (oldmode ||
113 (ldt_info.contents == 0 &&
114 ldt_info.read_exec_only == 1 &&
115 ldt_info.seg_32bit == 0 &&
116 ldt_info.limit_in_pages == 0 &&
117 ldt_info.seg_not_present == 1 &&
118 ldt_info.useable == 0 )) {
119 entry_1 = 0;
120 entry_2 = 0;
121 goto install;
122 }
123 }
124
125 entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
126 (ldt_info.limit & 0x0ffff);
127 entry_2 = (ldt_info.base_addr & 0xff000000) |
128 ((ldt_info.base_addr & 0x00ff0000) >> 16) |
129 (ldt_info.limit & 0xf0000) |
130 ((ldt_info.read_exec_only ^ 1) << 9) |
131 (ldt_info.contents << 10) |
132 ((ldt_info.seg_not_present ^ 1) << 15) |
133 (ldt_info.seg_32bit << 22) |
134 (ldt_info.limit_in_pages << 23) |
135 0x7000;
136 if (!oldmode)
137 entry_2 |= (ldt_info.useable << 20);
138
139
140install:
141 *lp = entry_1;
142 *(lp+1) = entry_2;
143 error = 0;
144
145out_unlock:
146 up_write(&mm->mmap_sem);
147out:
148 return error;
149}
150
151asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
152{
153 int ret = -ENOSYS;
154
155 switch (func) {
156 case 0:
157 ret = read_ldt(ptr, bytecount);
158 break;
159 case 1:
160 ret = write_ldt(ptr, bytecount, 1);
161 break;
162 case 2:
163 ret = read_default_ldt(ptr, bytecount);
164 break;
165 case 0x11:
166 ret = write_ldt(ptr, bytecount, 0);
167 break;
168 }
169 return ret;
170}
171