]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/user_ldt.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / osfmk / i386 / user_ldt.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
1c79356b 11 *
37839358
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * @OSF_COPYRIGHT@
24 */
25/*
26 * Mach Operating System
27 * Copyright (c) 1991 Carnegie Mellon University
28 * All Rights Reserved.
29 *
30 * Permission to use, copy, modify and distribute this software and its
31 * documentation is hereby granted, provided that both the copyright
32 * notice and this permission notice appear in all copies of the
33 * software, derivative works or modified versions, and any portions
34 * thereof, and that both notices appear in supporting documentation.
35 *
36 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
39 *
40 * Carnegie Mellon requests users of this software to return to
41 *
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
46 *
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
49 */
50
51/*
52 */
53
54/*
55 * User LDT management.
56 * Each thread in a task may have its own LDT.
57 */
58
59#include <kern/kalloc.h>
60#include <kern/thread.h>
61#include <kern/misc_protos.h>
62
63#include <vm/vm_kern.h>
64
65#include <i386/seg.h>
66#include <i386/thread.h>
67#include <i386/user_ldt.h>
68
69char acc_type[8][3] = {
70 /* code stack data */
71 { 0, 0, 1 }, /* data */
72 { 0, 1, 1 }, /* data, writable */
73 { 0, 0, 1 }, /* data, expand-down */
74 { 0, 1, 1 }, /* data, writable, expand-down */
75 { 1, 0, 0 }, /* code */
76 { 1, 0, 1 }, /* code, readable */
77 { 1, 0, 0 }, /* code, conforming */
78 { 1, 0, 1 }, /* code, readable, conforming */
79};
80
1c79356b
A
81#if 0
82/* Forward */
83
84extern boolean_t selector_check(
85 thread_t thread,
86 int sel,
87 int type);
88
89boolean_t
90selector_check(
91 thread_t thread,
92 int sel,
93 int type)
94{
95 struct user_ldt *ldt;
96 int access;
97
91447636 98 ldt = thread->machine.pcb->ims.ldt;
1c79356b
A
99 if (ldt == 0) {
100 switch (type) {
101 case S_CODE:
102 return sel == USER_CS;
103 case S_STACK:
104 return sel == USER_DS;
105 case S_DATA:
106 return sel == 0 ||
107 sel == USER_CS ||
108 sel == USER_DS;
109 }
110 }
111
112 if (type != S_DATA && sel == 0)
113 return FALSE;
114 if ((sel & (SEL_LDTS|SEL_PL)) != (SEL_LDTS|SEL_PL_U)
115 || sel > ldt->desc.limit_low)
116 return FALSE;
117
118 access = ldt->ldt[sel_idx(sel)].access;
119
120 if ((access & (ACC_P|ACC_PL|ACC_TYPE_USER))
121 != (ACC_P|ACC_PL_U|ACC_TYPE_USER))
122 return FALSE;
123 /* present, pl == pl.user, not system */
124
125 return acc_type[(access & 0xe)>>1][type];
126}
127
128/*
129 * Add the descriptors to the LDT, starting with
130 * the descriptor for 'first_selector'.
131 */
132
133kern_return_t
134i386_set_ldt(
91447636 135 thread_t thr_act,
1c79356b
A
136 int first_selector,
137 descriptor_list_t desc_list,
138 mach_msg_type_number_t count)
139{
140 user_ldt_t new_ldt, old_ldt, temp;
141 struct real_descriptor *dp;
142 int i;
143 int min_selector = 0;
144 pcb_t pcb;
145 vm_size_t ldt_size_needed;
146 int first_desc = sel_idx(first_selector);
147 vm_map_copy_t old_copy_object;
148 thread_t thread;
149
150 if (first_desc < min_selector || first_desc > 8191)
151 return KERN_INVALID_ARGUMENT;
152 if (first_desc + count >= 8192)
153 return KERN_INVALID_ARGUMENT;
91447636 154 if (thr_act == THREAD_NULL)
1c79356b
A
155 return KERN_INVALID_ARGUMENT;
156 if ((thread = act_lock_thread(thr_act)) == THREAD_NULL) {
157 act_unlock_thread(thr_act);
158 return KERN_INVALID_ARGUMENT;
159 }
160 if (thread == current_thread())
161 min_selector = LDTSZ;
162 act_unlock_thread(thr_act);
163
164 /*
165 * We must copy out desc_list to the kernel map, and wire
166 * it down (we touch it while the PCB is locked).
167 *
168 * We make a copy of the copyin object, and clear
169 * out the old one, so that the MIG stub will have a
170 * a empty (but valid) copyin object to discard.
171 */
172 {
173 kern_return_t kr;
91447636 174 vm_map_offset_t dst_addr;
1c79356b
A
175
176 old_copy_object = (vm_map_copy_t) desc_list;
177
178 kr = vm_map_copyout(ipc_kernel_map, &dst_addr,
179 vm_map_copy_copy(old_copy_object));
180 if (kr != KERN_SUCCESS)
181 return kr;
182
183 (void) vm_map_wire(ipc_kernel_map,
91447636
A
184 vm_map_trunc_page(dst_addr),
185 vm_map_round_page(dst_addr +
1c79356b
A
186 count * sizeof(struct real_descriptor)),
187 VM_PROT_READ|VM_PROT_WRITE, FALSE);
91447636 188 desc_list = CAST_DOWN(descriptor_list_t, dst_addr);
1c79356b
A
189 }
190
191 for (i = 0, dp = (struct real_descriptor *) desc_list;
192 i < count;
193 i++, dp++)
194 {
195 switch (dp->access & ~ACC_A) {
196 case 0:
197 case ACC_P:
198 /* valid empty descriptor */
199 break;
200 case ACC_P | ACC_CALL_GATE:
201 /* Mach kernel call */
202 *dp = *(struct real_descriptor *)
203 &ldt[sel_idx(USER_SCALL)];
204 break;
205 case ACC_P | ACC_PL_U | ACC_DATA:
206 case ACC_P | ACC_PL_U | ACC_DATA_W:
207 case ACC_P | ACC_PL_U | ACC_DATA_E:
208 case ACC_P | ACC_PL_U | ACC_DATA_EW:
209 case ACC_P | ACC_PL_U | ACC_CODE:
210 case ACC_P | ACC_PL_U | ACC_CODE_R:
211 case ACC_P | ACC_PL_U | ACC_CODE_C:
212 case ACC_P | ACC_PL_U | ACC_CODE_CR:
213 case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
214 case ACC_P | ACC_PL_U | ACC_CALL_GATE:
215 break;
216 default:
217 (void) vm_map_remove(ipc_kernel_map,
91447636
A
218 vm_map_trunc_page(desc_list),
219 vm_map_round_page(&desc_list[count]),
1c79356b
A
220 VM_MAP_REMOVE_KUNWIRE);
221 return KERN_INVALID_ARGUMENT;
222 }
223 }
224 ldt_size_needed = sizeof(struct real_descriptor)
225 * (first_desc + count);
226
91447636 227 pcb = thr_act->machine.pcb;
1c79356b
A
228 new_ldt = 0;
229 Retry:
230 simple_lock(&pcb->lock);
231 old_ldt = pcb->ims.ldt;
232 if (old_ldt == 0 ||
233 old_ldt->desc.limit_low + 1 < ldt_size_needed)
234 {
235 /*
236 * No old LDT, or not big enough
237 */
238 if (new_ldt == 0) {
239 simple_unlock(&pcb->lock);
240
241 new_ldt = (user_ldt_t) kalloc(ldt_size_needed
242 + sizeof(struct real_descriptor));
243 new_ldt->desc.limit_low = ldt_size_needed - 1;
244 new_ldt->desc.limit_high = 0;
245 new_ldt->desc.base_low =
246 ((vm_offset_t)&new_ldt->ldt[0]) & 0xffff;
247 new_ldt->desc.base_med =
248 (((vm_offset_t)&new_ldt->ldt[0]) >> 16)
249 & 0xff;
250 new_ldt->desc.base_high =
251 ((vm_offset_t)&new_ldt->ldt[0]) >> 24;
252 new_ldt->desc.access = ACC_P | ACC_LDT;
253 new_ldt->desc.granularity = 0;
254
255 goto Retry;
256 }
257
258 /*
259 * Have new LDT. If there was a an old ldt, copy descriptors
260 * from old to new. Otherwise copy the default ldt.
261 */
262 if (old_ldt) {
263 bcopy((char *)&old_ldt->ldt[0],
264 (char *)&new_ldt->ldt[0],
265 old_ldt->desc.limit_low + 1);
266 }
91447636 267 else if (thr_act == current_thread()) {
1c79356b
A
268 struct real_descriptor template = {0, 0, 0, ACC_P, 0, 0 ,0};
269
270 for (dp = &new_ldt->ldt[0], i = 0; i < first_desc; i++, dp++) {
271 if (i < LDTSZ)
272 *dp = *(struct real_descriptor *) &ldt[i];
273 else
274 *dp = template;
275 }
276 }
277
278 temp = old_ldt;
279 old_ldt = new_ldt; /* use new LDT from now on */
280 new_ldt = temp; /* discard old LDT */
281
282 pcb->ims.ldt = old_ldt; /* new LDT for thread */
283 }
284
285 /*
286 * Install new descriptors.
287 */
288 bcopy((char *)desc_list,
289 (char *)&old_ldt->ldt[first_desc],
290 count * sizeof(struct real_descriptor));
291
292 simple_unlock(&pcb->lock);
293
294 if (new_ldt)
295 kfree((vm_offset_t)new_ldt,
296 new_ldt->desc.limit_low+1+sizeof(struct real_descriptor));
297
298 /*
299 * Free the descriptor list.
300 */
91447636
A
301 (void) vm_map_remove(ipc_kernel_map, vm_map_trunc_page(desc_list),
302 vm_map_round_page(&desc_list[count]),
303 VM_MAP_REMOVE_KUNWIRE);
1c79356b
A
304 return KERN_SUCCESS;
305}
306
307kern_return_t
308i386_get_ldt(
91447636 309 thread_t thr_act,
1c79356b
A
310 int first_selector,
311 int selector_count, /* number wanted */
312 descriptor_list_t *desc_list, /* in/out */
313 mach_msg_type_number_t *count) /* in/out */
314{
315 struct user_ldt *user_ldt;
91447636 316 pcb_t pcb = thr_act->machine.pcb;
1c79356b
A
317 int first_desc = sel_idx(first_selector);
318 unsigned int ldt_count;
319 vm_size_t ldt_size;
320 vm_size_t size, size_needed;
321 vm_offset_t addr;
322 thread_t thread;
323
91447636 324 if (thr_act == THREAD_NULL)
1c79356b
A
325 return KERN_INVALID_ARGUMENT;
326
327 if (first_desc < 0 || first_desc > 8191)
328 return KERN_INVALID_ARGUMENT;
329 if (first_desc + selector_count >= 8192)
330 return KERN_INVALID_ARGUMENT;
331
332 addr = 0;
333 size = 0;
334
335 for (;;) {
336 simple_lock(&pcb->lock);
337 user_ldt = pcb->ims.ldt;
338 if (user_ldt == 0) {
339 simple_unlock(&pcb->lock);
340 if (addr)
341 kmem_free(ipc_kernel_map, addr, size);
342 *count = 0;
343 return KERN_SUCCESS;
344 }
345
346 /*
347 * Find how many descriptors we should return.
348 */
349 ldt_count = (user_ldt->desc.limit_low + 1) /
350 sizeof (struct real_descriptor);
351 ldt_count -= first_desc;
352 if (ldt_count > selector_count)
353 ldt_count = selector_count;
354
355 ldt_size = ldt_count * sizeof(struct real_descriptor);
356
357 /*
358 * Do we have the memory we need?
359 */
360 if (ldt_count <= *count)
361 break; /* fits in-line */
362
363 size_needed = round_page(ldt_size);
364 if (size_needed <= size)
365 break;
366
367 /*
368 * Unlock the pcb and allocate more memory
369 */
370 simple_unlock(&pcb->lock);
371
372 if (size != 0)
373 kmem_free(ipc_kernel_map, addr, size);
374
375 size = size_needed;
376
377 if (kmem_alloc(ipc_kernel_map, &addr, size)
378 != KERN_SUCCESS)
379 return KERN_RESOURCE_SHORTAGE;
380 }
381
382 /*
383 * copy out the descriptors
384 */
385 bcopy((char *)&user_ldt->ldt[first_desc],
386 (char *)addr,
387 ldt_size);
388 *count = ldt_count;
389 simple_unlock(&pcb->lock);
390
391 if (addr) {
392 vm_size_t size_used, size_left;
393 vm_map_copy_t memory;
394
395 /*
396 * Free any unused memory beyond the end of the last page used
397 */
398 size_used = round_page(ldt_size);
399 if (size_used != size)
400 kmem_free(ipc_kernel_map,
401 addr + size_used, size - size_used);
402
403 /*
404 * Zero the remainder of the page being returned.
405 */
406 size_left = size_used - ldt_size;
407 if (size_left > 0)
408 bzero((char *)addr + ldt_size, size_left);
409
410 /*
411 * Unwire the memory and make it into copyin form.
412 */
91447636
A
413 (void) vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr),
414 vm_map_round_page(addr + size_used), FALSE);
415 (void) vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr,
416 (vm_map_size_t)size_used, TRUE, &memory);
1c79356b
A
417 *desc_list = (descriptor_list_t) memory;
418 }
419
420 return KERN_SUCCESS;
421}
422
423#endif
424void
425user_ldt_free(
426 user_ldt_t user_ldt)
427{
91447636 428 kfree(user_ldt,
1c79356b
A
429 user_ldt->desc.limit_low+1+sizeof(struct real_descriptor));
430}