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/vmalloc.h>
14
15#include <asm/uaccess.h>
16
17#include "ia32priv.h"
18
19
20
21
22
23
24static int
25read_ldt (void __user *ptr, unsigned long bytecount)
26{
27 unsigned long bytes_left, n;
28 char __user *src, *dst;
29 char buf[256];
30
31 if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE)
32 bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE;
33
34 bytes_left = bytecount;
35
36 src = (void __user *) IA32_LDT_OFFSET;
37 dst = ptr;
38
39 while (bytes_left) {
40 n = sizeof(buf);
41 if (n > bytes_left)
42 n = bytes_left;
43
44
45
46
47
48 if (__copy_from_user(buf, src, n))
49 return -EFAULT;
50
51 if (copy_to_user(dst, buf, n))
52 return -EFAULT;
53
54 src += n;
55 dst += n;
56 bytes_left -= n;
57 }
58 return bytecount;
59}
60
61static int
62read_default_ldt (void __user * ptr, unsigned long bytecount)
63{
64 unsigned long size;
65 int err;
66
67
68 err = 0;
69 size = 8;
70 if (size > bytecount)
71 size = bytecount;
72
73 err = size;
74 if (clear_user(ptr, size))
75 err = -EFAULT;
76
77 return err;
78}
79
80static int
81write_ldt (void __user * ptr, unsigned long bytecount, int oldmode)
82{
83 struct ia32_user_desc ldt_info;
84 __u64 entry;
85 int ret;
86
87 if (bytecount != sizeof(ldt_info))
88 return -EINVAL;
89 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
90 return -EFAULT;
91
92 if (ldt_info.entry_number >= IA32_LDT_ENTRIES)
93 return -EINVAL;
94 if (ldt_info.contents == 3) {
95 if (oldmode)
96 return -EINVAL;
97 if (ldt_info.seg_not_present == 0)
98 return -EINVAL;
99 }
100
101 if (ldt_info.base_addr == 0 && ldt_info.limit == 0
102 && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1
103 && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0
104 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0)))
105
106 entry = 0;
107 else
108
109 entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit,
110 (((ldt_info.read_exec_only ^ 1) << 1)
111 | (ldt_info.contents << 2)) | 1,
112 1, 3, ldt_info.seg_not_present ^ 1,
113 (oldmode ? 0 : ldt_info.useable),
114 ldt_info.seg_32bit,
115 ldt_info.limit_in_pages);
116
117
118
119
120
121 ret = __put_user(entry, (__u64 __user *) IA32_LDT_OFFSET + ldt_info.entry_number);
122 ia32_load_segment_descriptors(current);
123 return ret;
124}
125
126asmlinkage int
127sys32_modify_ldt (int func, unsigned int ptr, unsigned int bytecount)
128{
129 int ret = -ENOSYS;
130
131 switch (func) {
132 case 0:
133 ret = read_ldt(compat_ptr(ptr), bytecount);
134 break;
135 case 1:
136 ret = write_ldt(compat_ptr(ptr), bytecount, 1);
137 break;
138 case 2:
139 ret = read_default_ldt(compat_ptr(ptr), bytecount);
140 break;
141 case 0x11:
142 ret = write_ldt(compat_ptr(ptr), bytecount, 0);
143 break;
144 }
145 return ret;
146}
147