FreeBSD/Linux Kernel Cross Reference
sys/i386/user_ldt.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1993,1992,1991 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26
27 /*
28 * HISTORY
29 * $Log: user_ldt.c,v $
30 * Revision 2.4 93/11/17 16:40:43 dbg
31 * Fixed bug in i386_set_ldt: descriptors were being copied into
32 * the old LDT (which was sometimes NULL), thus being thrown away
33 * (!).
34 * [93/09/10 dbg]
35 * Removed lint.
36 * [93/06/17 dbg]
37 *
38 * Revision 2.3 92/02/23 19:45:12 elf
39 * Eliminate keep_wired argument from vm_map_copyin().
40 * [92/02/23 danner]
41 *
42 * Revision 2.2 92/01/03 20:10:02 dbg
43 * Created.
44 * [91/08/20 dbg]
45 *
46 */
47
48 /*
49 * User LDT management.
50 * Each thread in a task may have its own LDT.
51 */
52
53 #include <kern/kalloc.h>
54 #include <kern/memory.h>
55 #include <kern/thread.h>
56
57 #include <vm/vm_kern.h>
58
59 #include <i386/seg.h>
60 #include <i386/thread.h>
61 #include <i386/user_ldt.h>
62
63 char acc_type[8][3] = {
64 /* code stack data */
65 { 0, 0, 1 }, /* data */
66 { 0, 1, 1 }, /* data, writable */
67 { 0, 0, 1 }, /* data, expand-down */
68 { 0, 1, 1 }, /* data, writable, expand-down */
69 { 1, 0, 0 }, /* code */
70 { 1, 0, 1 }, /* code, readable */
71 { 1, 0, 0 }, /* code, conforming */
72 { 1, 0, 1 }, /* code, readable, conforming */
73 };
74
75 extern struct fake_descriptor ldt[]; /* for system call gate */
76
77 boolean_t selector_check(
78 thread_t thread,
79 int sel,
80 int type) /* code, stack, data */
81 {
82 struct user_ldt *ldt;
83 int access;
84
85 ldt = thread->pcb->ims.ldt;
86 if (ldt == 0) {
87 switch (type) {
88 case S_CODE:
89 return sel == USER_CS;
90 case S_STACK:
91 return sel == USER_DS;
92 case S_DATA:
93 return sel == 0 ||
94 sel == USER_CS ||
95 sel == USER_DS;
96 }
97 }
98
99 if (type != S_DATA && sel == 0)
100 return FALSE;
101 if ((sel & (SEL_LDT|SEL_PL)) != (SEL_LDT|SEL_PL_U)
102 || sel > ldt->desc.limit_low)
103 return FALSE;
104
105 access = ldt->ldt[sel_idx(sel)].access;
106
107 if ((access & (ACC_P|ACC_PL|ACC_TYPE_USER))
108 != (ACC_P|ACC_PL_U|ACC_TYPE_USER))
109 return FALSE;
110 /* present, pl == pl.user, not system */
111
112 return acc_type[(access & 0xe)>>1][type];
113 }
114
115 /*
116 * Add the descriptors to the LDT, starting with
117 * the descriptor for 'first_selector'.
118 */
119 kern_return_t
120 i386_set_ldt(
121 thread_t thread,
122 int first_selector,
123 struct real_descriptor *desc_list,
124 natural_t count,
125 boolean_t desc_list_inline)
126 {
127 user_ldt_t new_ldt, old_ldt, temp;
128 struct real_descriptor *dp;
129 int i;
130 pcb_t pcb;
131 vm_size_t ldt_size_needed;
132 int first_desc = sel_idx(first_selector);
133 vm_map_copy_t old_copy_object;
134
135 if (thread == THREAD_NULL)
136 return KERN_INVALID_ARGUMENT;
137 if (first_desc < 0 || first_desc > 8191)
138 return KERN_INVALID_ARGUMENT;
139 if (first_desc + count >= 8192)
140 return KERN_INVALID_ARGUMENT;
141
142 /*
143 * If desc_list is not inline, it is in copyin form.
144 * We must copy it out to the kernel map, and wire
145 * it down (we touch it while the PCB is locked).
146 *
147 * We make a copy of the copyin object, and clear
148 * out the old one, so that returning KERN_INVALID_ARGUMENT
149 * will not try to deallocate the data twice.
150 */
151 if (!desc_list_inline) {
152 kern_return_t kr;
153 vm_offset_t dst_addr;
154
155 old_copy_object = (vm_map_copy_t) desc_list;
156
157 kr = vm_map_copyout(ipc_kernel_map, &dst_addr,
158 vm_map_copy_copy(old_copy_object));
159 if (kr != KERN_SUCCESS)
160 return kr;
161
162 (void) vm_map_pageable(ipc_kernel_map,
163 dst_addr,
164 dst_addr + count * sizeof(struct real_descriptor),
165 VM_PROT_READ|VM_PROT_WRITE);
166 desc_list = (struct real_descriptor *)dst_addr;
167 }
168
169 for (i = 0, dp = desc_list;
170 i < count;
171 i++, dp++)
172 {
173 switch (dp->access & ~ACC_A) {
174 case 0:
175 case ACC_P:
176 /* valid empty descriptor */
177 break;
178 case ACC_P | ACC_CALL_GATE:
179 /* Mach kernel call */
180 *dp = *(struct real_descriptor *)
181 &ldt[sel_idx(USER_SCALL)];
182 break;
183 case ACC_P | ACC_PL_U | ACC_DATA:
184 case ACC_P | ACC_PL_U | ACC_DATA_W:
185 case ACC_P | ACC_PL_U | ACC_DATA_E:
186 case ACC_P | ACC_PL_U | ACC_DATA_EW:
187 case ACC_P | ACC_PL_U | ACC_CODE:
188 case ACC_P | ACC_PL_U | ACC_CODE_R:
189 case ACC_P | ACC_PL_U | ACC_CODE_C:
190 case ACC_P | ACC_PL_U | ACC_CODE_CR:
191 case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
192 case ACC_P | ACC_PL_U | ACC_CALL_GATE:
193 break;
194 default:
195 return KERN_INVALID_ARGUMENT;
196 }
197 }
198 ldt_size_needed = sizeof(struct real_descriptor)
199 * (first_desc + count - 1);
200
201 pcb = thread->pcb;
202 new_ldt = 0;
203 Retry:
204 simple_lock(&pcb->lock);
205 old_ldt = pcb->ims.ldt;
206 if (old_ldt == 0 ||
207 old_ldt->desc.limit_low + 1 < ldt_size_needed)
208 {
209 /*
210 * No old LDT, or not big enough
211 */
212 if (new_ldt == 0) {
213 simple_unlock(&pcb->lock);
214
215 new_ldt = (user_ldt_t)
216 kalloc(ldt_size_needed
217 + sizeof(struct real_descriptor));
218 new_ldt->desc.limit_low = ldt_size_needed - 1;
219 new_ldt->desc.limit_high = 0;
220 new_ldt->desc.base_low = ((vm_offset_t)new_ldt) & 0xffff;
221 new_ldt->desc.base_med = (((vm_offset_t)new_ldt) >> 16)
222 & 0xff;
223 new_ldt->desc.base_high = ((vm_offset_t)new_ldt) >> 24;
224 new_ldt->desc.access = ACC_P | ACC_LDT;
225 new_ldt->desc.granularity = 0;
226
227 goto Retry;
228 }
229
230 /*
231 * Have new LDT. Copy descriptors from old to new.
232 */
233 if (old_ldt)
234 bcopy(&old_ldt->ldt[0],
235 &new_ldt->ldt[0],
236 old_ldt->desc.limit_low + 1);
237
238 temp = old_ldt;
239 old_ldt = new_ldt; /* use new LDT from now on */
240 new_ldt = temp; /* discard old LDT */
241
242 pcb->ims.ldt = old_ldt; /* new LDT for thread */
243 }
244
245 /*
246 * Install new descriptors.
247 */
248 bcopy(desc_list,
249 &new_ldt->ldt[first_desc],
250 count * sizeof(struct real_descriptor));
251
252 simple_unlock(&pcb->lock);
253
254 if (old_ldt)
255 kfree((vm_offset_t)old_ldt,
256 old_ldt->desc.limit_low + 1
257 + sizeof(struct real_descriptor));
258
259 /*
260 * Free the descriptor list, if it was
261 * out-of-line. Also discard the original
262 * copy object for it.
263 */
264 if (!desc_list_inline) {
265 (void) kmem_free(ipc_kernel_map,
266 (vm_offset_t) desc_list,
267 count * sizeof(struct real_descriptor));
268 vm_map_copy_discard(old_copy_object);
269 }
270
271 return KERN_SUCCESS;
272 }
273
274 kern_return_t
275 i386_get_ldt(
276 thread_t thread,
277 int first_selector,
278 int selector_count, /* number wanted */
279 struct real_descriptor **desc_list, /* in/out */
280 natural_t *count) /* in/out */
281 {
282 struct user_ldt *user_ldt;
283 pcb_t pcb = thread->pcb;
284 int first_desc = sel_idx(first_selector);
285 unsigned int ldt_count;
286 vm_size_t ldt_size;
287 vm_size_t size, size_needed;
288 vm_offset_t addr;
289
290 if (thread == THREAD_NULL)
291 return KERN_INVALID_ARGUMENT;
292 if (first_desc < 0 || first_desc > 8191)
293 return KERN_INVALID_ARGUMENT;
294 if (first_desc + selector_count >= 8192)
295 return KERN_INVALID_ARGUMENT;
296
297 addr = 0;
298 size = 0;
299
300 for (;;) {
301 simple_lock(&pcb->lock);
302 user_ldt = pcb->ims.ldt;
303 if (user_ldt == 0) {
304 simple_unlock(&pcb->lock);
305 if (addr)
306 kmem_free(ipc_kernel_map, addr, size);
307 *count = 0;
308 return KERN_SUCCESS;
309 }
310
311 /*
312 * Find how many descriptors we should return.
313 */
314 ldt_count = (user_ldt->desc.limit_low + 1) /
315 sizeof (struct real_descriptor);
316 ldt_count -= first_desc;
317 if (ldt_count > selector_count)
318 ldt_count = selector_count;
319
320 ldt_size = ldt_count * sizeof(struct real_descriptor);
321
322 /*
323 * Do we have the memory we need?
324 */
325 if (ldt_count <= *count)
326 break; /* fits in-line */
327
328 size_needed = round_page(ldt_size);
329 if (size_needed <= size)
330 break;
331
332 /*
333 * Unlock the pcb and allocate more memory
334 */
335 simple_unlock(&pcb->lock);
336
337 if (size != 0)
338 kmem_free(ipc_kernel_map, addr, size);
339
340 size = size_needed;
341
342 if (kmem_alloc(ipc_kernel_map, &addr, size)
343 != KERN_SUCCESS)
344 return KERN_RESOURCE_SHORTAGE;
345 }
346
347 /*
348 * copy out the descriptors
349 */
350 bcopy(&user_ldt[first_desc],
351 *desc_list,
352 ldt_size);
353 *count = ldt_count;
354 simple_unlock(&pcb->lock);
355
356 if (addr) {
357 vm_size_t size_used, size_left;
358 vm_map_copy_t memory;
359
360 /*
361 * Free any unused memory beyond the end of the last page used
362 */
363 size_used = round_page(ldt_size);
364 if (size_used != size)
365 kmem_free(ipc_kernel_map,
366 addr + size_used, size - size_used);
367
368 /*
369 * Zero the remainder of the page being returned.
370 */
371 size_left = size_used - ldt_size;
372 if (size_left > 0)
373 bzero((void *)addr + ldt_size, size_left);
374
375 /*
376 * Make memory into copyin form - this unwires it.
377 */
378 (void) vm_map_copyin(ipc_kernel_map, addr, size_used, TRUE, &memory);
379 *desc_list = (struct real_descriptor *)memory;
380 }
381
382 return KERN_SUCCESS;
383 }
384
385 void
386 user_ldt_free(
387 user_ldt_t user_ldt)
388 {
389 kfree((vm_offset_t)user_ldt,
390 user_ldt->desc.limit_low + 1
391 + sizeof(struct real_descriptor));
392 }
Cache object: 648a7782d56767079706fd7a75ec88ba
|