xref: /linux/arch/x86/kernel/ldt.c (revision 7265706c8fd57722f622f336ec110cb35f83e739)
1 /*
2  * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
3  * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
4  * Copyright (C) 2002 Andi Kleen
5  *
6  * This handles calls from both 32bit and 64bit mode.
7  */
8 
9 #include <linux/errno.h>
10 #include <linux/sched.h>
11 #include <linux/string.h>
12 #include <linux/mm.h>
13 #include <linux/smp.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 #include <asm/mmu_context.h>
21 
22 #ifdef CONFIG_SMP
23 static void flush_ldt(void *current_mm)
24 {
25 	if (current->active_mm == current_mm)
26 		load_LDT(&current->active_mm->context);
27 }
28 #endif
29 
30 static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
31 {
32 	void *oldldt, *newldt;
33 	int oldsize;
34 
35 	if (mincount <= pc->size)
36 		return 0;
37 	oldsize = pc->size;
38 	mincount = (mincount + (PAGE_SIZE / LDT_ENTRY_SIZE - 1)) &
39 			(~(PAGE_SIZE / LDT_ENTRY_SIZE - 1));
40 	if (mincount * LDT_ENTRY_SIZE > PAGE_SIZE)
41 		newldt = vmalloc(mincount * LDT_ENTRY_SIZE);
42 	else
43 		newldt = (void *)__get_free_page(GFP_KERNEL);
44 
45 	if (!newldt)
46 		return -ENOMEM;
47 
48 	if (oldsize)
49 		memcpy(newldt, pc->ldt, oldsize * LDT_ENTRY_SIZE);
50 	oldldt = pc->ldt;
51 	memset(newldt + oldsize * LDT_ENTRY_SIZE, 0,
52 	       (mincount - oldsize) * LDT_ENTRY_SIZE);
53 
54 #ifdef CONFIG_X86_64
55 	/* CHECKME: Do we really need this ? */
56 	wmb();
57 #endif
58 	pc->ldt = newldt;
59 	wmb();
60 	pc->size = mincount;
61 	wmb();
62 
63 	if (reload) {
64 #ifdef CONFIG_SMP
65 		preempt_disable();
66 		load_LDT(pc);
67 		if (!cpus_equal(current->mm->cpu_vm_mask,
68 				cpumask_of_cpu(smp_processor_id())))
69 			smp_call_function(flush_ldt, current->mm, 1);
70 		preempt_enable();
71 #else
72 		load_LDT(pc);
73 #endif
74 	}
75 	if (oldsize) {
76 		if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE)
77 			vfree(oldldt);
78 		else
79 			put_page(virt_to_page(oldldt));
80 	}
81 	return 0;
82 }
83 
84 static inline int copy_ldt(mm_context_t *new, mm_context_t *old)
85 {
86 	int err = alloc_ldt(new, old->size, 0);
87 
88 	if (err < 0)
89 		return err;
90 	memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE);
91 	return 0;
92 }
93 
94 /*
95  * we do not have to muck with descriptors here, that is
96  * done in switch_mm() as needed.
97  */
98 int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
99 {
100 	struct mm_struct *old_mm;
101 	int retval = 0;
102 
103 	mutex_init(&mm->context.lock);
104 	mm->context.size = 0;
105 	old_mm = current->mm;
106 	if (old_mm && old_mm->context.size > 0) {
107 		mutex_lock(&old_mm->context.lock);
108 		retval = copy_ldt(&mm->context, &old_mm->context);
109 		mutex_unlock(&old_mm->context.lock);
110 	}
111 	return retval;
112 }
113 
114 /*
115  * No need to lock the MM as we are the last user
116  *
117  * 64bit: Don't touch the LDT register - we're already in the next thread.
118  */
119 void destroy_context(struct mm_struct *mm)
120 {
121 	if (mm->context.size) {
122 #ifdef CONFIG_X86_32
123 		/* CHECKME: Can this ever happen ? */
124 		if (mm == current->active_mm)
125 			clear_LDT();
126 #endif
127 		if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE)
128 			vfree(mm->context.ldt);
129 		else
130 			put_page(virt_to_page(mm->context.ldt));
131 		mm->context.size = 0;
132 	}
133 }
134 
135 static int read_ldt(void __user *ptr, unsigned long bytecount)
136 {
137 	int err;
138 	unsigned long size;
139 	struct mm_struct *mm = current->mm;
140 
141 	if (!mm->context.size)
142 		return 0;
143 	if (bytecount > LDT_ENTRY_SIZE * LDT_ENTRIES)
144 		bytecount = LDT_ENTRY_SIZE * LDT_ENTRIES;
145 
146 	mutex_lock(&mm->context.lock);
147 	size = mm->context.size * LDT_ENTRY_SIZE;
148 	if (size > bytecount)
149 		size = bytecount;
150 
151 	err = 0;
152 	if (copy_to_user(ptr, mm->context.ldt, size))
153 		err = -EFAULT;
154 	mutex_unlock(&mm->context.lock);
155 	if (err < 0)
156 		goto error_return;
157 	if (size != bytecount) {
158 		/* zero-fill the rest */
159 		if (clear_user(ptr + size, bytecount - size) != 0) {
160 			err = -EFAULT;
161 			goto error_return;
162 		}
163 	}
164 	return bytecount;
165 error_return:
166 	return err;
167 }
168 
169 static int read_default_ldt(void __user *ptr, unsigned long bytecount)
170 {
171 	/* CHECKME: Can we use _one_ random number ? */
172 #ifdef CONFIG_X86_32
173 	unsigned long size = 5 * sizeof(struct desc_struct);
174 #else
175 	unsigned long size = 128;
176 #endif
177 	if (bytecount > size)
178 		bytecount = size;
179 	if (clear_user(ptr, bytecount))
180 		return -EFAULT;
181 	return bytecount;
182 }
183 
184 static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode)
185 {
186 	struct mm_struct *mm = current->mm;
187 	struct desc_struct ldt;
188 	int error;
189 	struct user_desc ldt_info;
190 
191 	error = -EINVAL;
192 	if (bytecount != sizeof(ldt_info))
193 		goto out;
194 	error = -EFAULT;
195 	if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
196 		goto out;
197 
198 	error = -EINVAL;
199 	if (ldt_info.entry_number >= LDT_ENTRIES)
200 		goto out;
201 	if (ldt_info.contents == 3) {
202 		if (oldmode)
203 			goto out;
204 		if (ldt_info.seg_not_present == 0)
205 			goto out;
206 	}
207 
208 	mutex_lock(&mm->context.lock);
209 	if (ldt_info.entry_number >= mm->context.size) {
210 		error = alloc_ldt(&current->mm->context,
211 				  ldt_info.entry_number + 1, 1);
212 		if (error < 0)
213 			goto out_unlock;
214 	}
215 
216 	/* Allow LDTs to be cleared by the user. */
217 	if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
218 		if (oldmode || LDT_empty(&ldt_info)) {
219 			memset(&ldt, 0, sizeof(ldt));
220 			goto install;
221 		}
222 	}
223 
224 	fill_ldt(&ldt, &ldt_info);
225 	if (oldmode)
226 		ldt.avl = 0;
227 
228 	/* Install the new entry ...  */
229 install:
230 	write_ldt_entry(mm->context.ldt, ldt_info.entry_number, &ldt);
231 	error = 0;
232 
233 out_unlock:
234 	mutex_unlock(&mm->context.lock);
235 out:
236 	return error;
237 }
238 
239 asmlinkage int sys_modify_ldt(int func, void __user *ptr,
240 			      unsigned long bytecount)
241 {
242 	int ret = -ENOSYS;
243 
244 	switch (func) {
245 	case 0:
246 		ret = read_ldt(ptr, bytecount);
247 		break;
248 	case 1:
249 		ret = write_ldt(ptr, bytecount, 1);
250 		break;
251 	case 2:
252 		ret = read_default_ldt(ptr, bytecount);
253 		break;
254 	case 0x11:
255 		ret = write_ldt(ptr, bytecount, 0);
256 		break;
257 	}
258 	return ret;
259 }
260