2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
34 * Mach Operating System
35 * Copyright (c) 1991 Carnegie Mellon University
36 * All Rights Reserved.
38 * Permission to use, copy, modify and distribute this software and its
39 * documentation is hereby granted, provided that both the copyright
40 * notice and this permission notice appear in all copies of the
41 * software, derivative works or modified versions, and any portions
42 * thereof, and that both notices appear in supporting documentation.
44 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
46 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
48 * Carnegie Mellon requests users of this software to return to
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
63 * User LDT management.
64 * Each thread in a task may have its own LDT.
67 #include <kern/kalloc.h>
68 #include <kern/thread.h>
69 #include <kern/misc_protos.h>
71 #include <vm/vm_kern.h>
74 #include <i386/thread.h>
75 #include <i386/user_ldt.h>
77 char acc_type
[8][3] = {
79 { 0, 0, 1 }, /* data */
80 { 0, 1, 1 }, /* data, writable */
81 { 0, 0, 1 }, /* data, expand-down */
82 { 0, 1, 1 }, /* data, writable, expand-down */
83 { 1, 0, 0 }, /* code */
84 { 1, 0, 1 }, /* code, readable */
85 { 1, 0, 0 }, /* code, conforming */
86 { 1, 0, 1 }, /* code, readable, conforming */
92 extern boolean_t
selector_check(
103 struct user_ldt
*ldt
;
106 ldt
= thread
->machine
.pcb
->ims
.ldt
;
110 return sel
== USER_CS
;
112 return sel
== USER_DS
;
120 if (type
!= S_DATA
&& sel
== 0)
122 if ((sel
& (SEL_LDTS
|SEL_PL
)) != (SEL_LDTS
|SEL_PL_U
)
123 || sel
> ldt
->desc
.limit_low
)
126 access
= ldt
->ldt
[sel_idx(sel
)].access
;
128 if ((access
& (ACC_P
|ACC_PL
|ACC_TYPE_USER
))
129 != (ACC_P
|ACC_PL_U
|ACC_TYPE_USER
))
131 /* present, pl == pl.user, not system */
133 return acc_type
[(access
& 0xe)>>1][type
];
137 * Add the descriptors to the LDT, starting with
138 * the descriptor for 'first_selector'.
145 descriptor_list_t desc_list
,
146 mach_msg_type_number_t count
)
148 user_ldt_t new_ldt
, old_ldt
, temp
;
149 struct real_descriptor
*dp
;
151 int min_selector
= 0;
153 vm_size_t ldt_size_needed
;
154 int first_desc
= sel_idx(first_selector
);
155 vm_map_copy_t old_copy_object
;
158 if (first_desc
< min_selector
|| first_desc
> 8191)
159 return KERN_INVALID_ARGUMENT
;
160 if (first_desc
+ count
>= 8192)
161 return KERN_INVALID_ARGUMENT
;
162 if (thr_act
== THREAD_NULL
)
163 return KERN_INVALID_ARGUMENT
;
164 if ((thread
= act_lock_thread(thr_act
)) == THREAD_NULL
) {
165 act_unlock_thread(thr_act
);
166 return KERN_INVALID_ARGUMENT
;
168 if (thread
== current_thread())
169 min_selector
= LDTSZ
;
170 act_unlock_thread(thr_act
);
173 * We must copy out desc_list to the kernel map, and wire
174 * it down (we touch it while the PCB is locked).
176 * We make a copy of the copyin object, and clear
177 * out the old one, so that the MIG stub will have a
178 * a empty (but valid) copyin object to discard.
182 vm_map_offset_t dst_addr
;
184 old_copy_object
= (vm_map_copy_t
) desc_list
;
186 kr
= vm_map_copyout(ipc_kernel_map
, &dst_addr
,
187 vm_map_copy_copy(old_copy_object
));
188 if (kr
!= KERN_SUCCESS
)
191 (void) vm_map_wire(ipc_kernel_map
,
192 vm_map_trunc_page(dst_addr
),
193 vm_map_round_page(dst_addr
+
194 count
* sizeof(struct real_descriptor
)),
195 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
196 desc_list
= CAST_DOWN(descriptor_list_t
, dst_addr
);
199 for (i
= 0, dp
= (struct real_descriptor
*) desc_list
;
203 switch (dp
->access
& ~ACC_A
) {
206 /* valid empty descriptor */
208 case ACC_P
| ACC_CALL_GATE
:
209 /* Mach kernel call */
210 *dp
= *(struct real_descriptor
*)
211 &ldt
[sel_idx(USER_SCALL
)];
213 case ACC_P
| ACC_PL_U
| ACC_DATA
:
214 case ACC_P
| ACC_PL_U
| ACC_DATA_W
:
215 case ACC_P
| ACC_PL_U
| ACC_DATA_E
:
216 case ACC_P
| ACC_PL_U
| ACC_DATA_EW
:
217 case ACC_P
| ACC_PL_U
| ACC_CODE
:
218 case ACC_P
| ACC_PL_U
| ACC_CODE_R
:
219 case ACC_P
| ACC_PL_U
| ACC_CODE_C
:
220 case ACC_P
| ACC_PL_U
| ACC_CODE_CR
:
221 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE_16
:
222 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE
:
225 (void) vm_map_remove(ipc_kernel_map
,
226 vm_map_trunc_page(desc_list
),
227 vm_map_round_page(&desc_list
[count
]),
228 VM_MAP_REMOVE_KUNWIRE
);
229 return KERN_INVALID_ARGUMENT
;
232 ldt_size_needed
= sizeof(struct real_descriptor
)
233 * (first_desc
+ count
);
235 pcb
= thr_act
->machine
.pcb
;
238 simple_lock(&pcb
->lock
);
239 old_ldt
= pcb
->ims
.ldt
;
241 old_ldt
->desc
.limit_low
+ 1 < ldt_size_needed
)
244 * No old LDT, or not big enough
247 simple_unlock(&pcb
->lock
);
249 new_ldt
= (user_ldt_t
) kalloc(ldt_size_needed
250 + sizeof(struct real_descriptor
));
251 new_ldt
->desc
.limit_low
= ldt_size_needed
- 1;
252 new_ldt
->desc
.limit_high
= 0;
253 new_ldt
->desc
.base_low
=
254 ((vm_offset_t
)&new_ldt
->ldt
[0]) & 0xffff;
255 new_ldt
->desc
.base_med
=
256 (((vm_offset_t
)&new_ldt
->ldt
[0]) >> 16)
258 new_ldt
->desc
.base_high
=
259 ((vm_offset_t
)&new_ldt
->ldt
[0]) >> 24;
260 new_ldt
->desc
.access
= ACC_P
| ACC_LDT
;
261 new_ldt
->desc
.granularity
= 0;
267 * Have new LDT. If there was a an old ldt, copy descriptors
268 * from old to new. Otherwise copy the default ldt.
271 bcopy((char *)&old_ldt
->ldt
[0],
272 (char *)&new_ldt
->ldt
[0],
273 old_ldt
->desc
.limit_low
+ 1);
275 else if (thr_act
== current_thread()) {
276 struct real_descriptor
template = {0, 0, 0, ACC_P
, 0, 0 ,0};
278 for (dp
= &new_ldt
->ldt
[0], i
= 0; i
< first_desc
; i
++, dp
++) {
280 *dp
= *(struct real_descriptor
*) &ldt
[i
];
287 old_ldt
= new_ldt
; /* use new LDT from now on */
288 new_ldt
= temp
; /* discard old LDT */
290 pcb
->ims
.ldt
= old_ldt
; /* new LDT for thread */
294 * Install new descriptors.
296 bcopy((char *)desc_list
,
297 (char *)&old_ldt
->ldt
[first_desc
],
298 count
* sizeof(struct real_descriptor
));
300 simple_unlock(&pcb
->lock
);
303 kfree((vm_offset_t
)new_ldt
,
304 new_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));
307 * Free the descriptor list.
309 (void) vm_map_remove(ipc_kernel_map
, vm_map_trunc_page(desc_list
),
310 vm_map_round_page(&desc_list
[count
]),
311 VM_MAP_REMOVE_KUNWIRE
);
319 int selector_count
, /* number wanted */
320 descriptor_list_t
*desc_list
, /* in/out */
321 mach_msg_type_number_t
*count
) /* in/out */
323 struct user_ldt
*user_ldt
;
324 pcb_t pcb
= thr_act
->machine
.pcb
;
325 int first_desc
= sel_idx(first_selector
);
326 unsigned int ldt_count
;
328 vm_size_t size
, size_needed
;
332 if (thr_act
== THREAD_NULL
)
333 return KERN_INVALID_ARGUMENT
;
335 if (first_desc
< 0 || first_desc
> 8191)
336 return KERN_INVALID_ARGUMENT
;
337 if (first_desc
+ selector_count
>= 8192)
338 return KERN_INVALID_ARGUMENT
;
344 simple_lock(&pcb
->lock
);
345 user_ldt
= pcb
->ims
.ldt
;
347 simple_unlock(&pcb
->lock
);
349 kmem_free(ipc_kernel_map
, addr
, size
);
355 * Find how many descriptors we should return.
357 ldt_count
= (user_ldt
->desc
.limit_low
+ 1) /
358 sizeof (struct real_descriptor
);
359 ldt_count
-= first_desc
;
360 if (ldt_count
> selector_count
)
361 ldt_count
= selector_count
;
363 ldt_size
= ldt_count
* sizeof(struct real_descriptor
);
366 * Do we have the memory we need?
368 if (ldt_count
<= *count
)
369 break; /* fits in-line */
371 size_needed
= round_page(ldt_size
);
372 if (size_needed
<= size
)
376 * Unlock the pcb and allocate more memory
378 simple_unlock(&pcb
->lock
);
381 kmem_free(ipc_kernel_map
, addr
, size
);
385 if (kmem_alloc(ipc_kernel_map
, &addr
, size
)
387 return KERN_RESOURCE_SHORTAGE
;
391 * copy out the descriptors
393 bcopy((char *)&user_ldt
->ldt
[first_desc
],
397 simple_unlock(&pcb
->lock
);
400 vm_size_t size_used
, size_left
;
401 vm_map_copy_t memory
;
404 * Free any unused memory beyond the end of the last page used
406 size_used
= round_page(ldt_size
);
407 if (size_used
!= size
)
408 kmem_free(ipc_kernel_map
,
409 addr
+ size_used
, size
- size_used
);
412 * Zero the remainder of the page being returned.
414 size_left
= size_used
- ldt_size
;
416 bzero((char *)addr
+ ldt_size
, size_left
);
419 * Unwire the memory and make it into copyin form.
421 (void) vm_map_unwire(ipc_kernel_map
, vm_map_trunc_page(addr
),
422 vm_map_round_page(addr
+ size_used
), FALSE
);
423 (void) vm_map_copyin(ipc_kernel_map
, (vm_map_address_t
)addr
,
424 (vm_map_size_t
)size_used
, TRUE
, &memory
);
425 *desc_list
= (descriptor_list_t
) memory
;
437 user_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));