2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1991 Carnegie Mellon University
28 * All Rights Reserved.
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.
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.
40 * Carnegie Mellon requests users of this software to return to
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
55 * User LDT management.
56 * Each thread in a task may have its own LDT.
59 #include <kern/kalloc.h>
60 #include <kern/thread.h>
61 #include <kern/misc_protos.h>
63 #include <vm/vm_kern.h>
66 #include <i386/thread.h>
67 #include <i386/user_ldt.h>
69 char acc_type
[8][3] = {
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 */
84 extern boolean_t
selector_check(
98 ldt
= thread
->machine
.pcb
->ims
.ldt
;
102 return sel
== USER_CS
;
104 return sel
== USER_DS
;
112 if (type
!= S_DATA
&& sel
== 0)
114 if ((sel
& (SEL_LDTS
|SEL_PL
)) != (SEL_LDTS
|SEL_PL_U
)
115 || sel
> ldt
->desc
.limit_low
)
118 access
= ldt
->ldt
[sel_idx(sel
)].access
;
120 if ((access
& (ACC_P
|ACC_PL
|ACC_TYPE_USER
))
121 != (ACC_P
|ACC_PL_U
|ACC_TYPE_USER
))
123 /* present, pl == pl.user, not system */
125 return acc_type
[(access
& 0xe)>>1][type
];
129 * Add the descriptors to the LDT, starting with
130 * the descriptor for 'first_selector'.
137 descriptor_list_t desc_list
,
138 mach_msg_type_number_t count
)
140 user_ldt_t new_ldt
, old_ldt
, temp
;
141 struct real_descriptor
*dp
;
143 int min_selector
= 0;
145 vm_size_t ldt_size_needed
;
146 int first_desc
= sel_idx(first_selector
);
147 vm_map_copy_t old_copy_object
;
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
;
154 if (thr_act
== THREAD_NULL
)
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
;
160 if (thread
== current_thread())
161 min_selector
= LDTSZ
;
162 act_unlock_thread(thr_act
);
165 * We must copy out desc_list to the kernel map, and wire
166 * it down (we touch it while the PCB is locked).
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.
174 vm_map_offset_t dst_addr
;
176 old_copy_object
= (vm_map_copy_t
) desc_list
;
178 kr
= vm_map_copyout(ipc_kernel_map
, &dst_addr
,
179 vm_map_copy_copy(old_copy_object
));
180 if (kr
!= KERN_SUCCESS
)
183 (void) vm_map_wire(ipc_kernel_map
,
184 vm_map_trunc_page(dst_addr
),
185 vm_map_round_page(dst_addr
+
186 count
* sizeof(struct real_descriptor
)),
187 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
188 desc_list
= CAST_DOWN(descriptor_list_t
, dst_addr
);
191 for (i
= 0, dp
= (struct real_descriptor
*) desc_list
;
195 switch (dp
->access
& ~ACC_A
) {
198 /* valid empty descriptor */
200 case ACC_P
| ACC_CALL_GATE
:
201 /* Mach kernel call */
202 *dp
= *(struct real_descriptor
*)
203 &ldt
[sel_idx(USER_SCALL
)];
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
:
217 (void) vm_map_remove(ipc_kernel_map
,
218 vm_map_trunc_page(desc_list
),
219 vm_map_round_page(&desc_list
[count
]),
220 VM_MAP_REMOVE_KUNWIRE
);
221 return KERN_INVALID_ARGUMENT
;
224 ldt_size_needed
= sizeof(struct real_descriptor
)
225 * (first_desc
+ count
);
227 pcb
= thr_act
->machine
.pcb
;
230 simple_lock(&pcb
->lock
);
231 old_ldt
= pcb
->ims
.ldt
;
233 old_ldt
->desc
.limit_low
+ 1 < ldt_size_needed
)
236 * No old LDT, or not big enough
239 simple_unlock(&pcb
->lock
);
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)
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;
259 * Have new LDT. If there was a an old ldt, copy descriptors
260 * from old to new. Otherwise copy the default ldt.
263 bcopy((char *)&old_ldt
->ldt
[0],
264 (char *)&new_ldt
->ldt
[0],
265 old_ldt
->desc
.limit_low
+ 1);
267 else if (thr_act
== current_thread()) {
268 struct real_descriptor
template = {0, 0, 0, ACC_P
, 0, 0 ,0};
270 for (dp
= &new_ldt
->ldt
[0], i
= 0; i
< first_desc
; i
++, dp
++) {
272 *dp
= *(struct real_descriptor
*) &ldt
[i
];
279 old_ldt
= new_ldt
; /* use new LDT from now on */
280 new_ldt
= temp
; /* discard old LDT */
282 pcb
->ims
.ldt
= old_ldt
; /* new LDT for thread */
286 * Install new descriptors.
288 bcopy((char *)desc_list
,
289 (char *)&old_ldt
->ldt
[first_desc
],
290 count
* sizeof(struct real_descriptor
));
292 simple_unlock(&pcb
->lock
);
295 kfree((vm_offset_t
)new_ldt
,
296 new_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));
299 * Free the descriptor list.
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
);
311 int selector_count
, /* number wanted */
312 descriptor_list_t
*desc_list
, /* in/out */
313 mach_msg_type_number_t
*count
) /* in/out */
315 struct user_ldt
*user_ldt
;
316 pcb_t pcb
= thr_act
->machine
.pcb
;
317 int first_desc
= sel_idx(first_selector
);
318 unsigned int ldt_count
;
320 vm_size_t size
, size_needed
;
324 if (thr_act
== THREAD_NULL
)
325 return KERN_INVALID_ARGUMENT
;
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
;
336 simple_lock(&pcb
->lock
);
337 user_ldt
= pcb
->ims
.ldt
;
339 simple_unlock(&pcb
->lock
);
341 kmem_free(ipc_kernel_map
, addr
, size
);
347 * Find how many descriptors we should return.
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
;
355 ldt_size
= ldt_count
* sizeof(struct real_descriptor
);
358 * Do we have the memory we need?
360 if (ldt_count
<= *count
)
361 break; /* fits in-line */
363 size_needed
= round_page(ldt_size
);
364 if (size_needed
<= size
)
368 * Unlock the pcb and allocate more memory
370 simple_unlock(&pcb
->lock
);
373 kmem_free(ipc_kernel_map
, addr
, size
);
377 if (kmem_alloc(ipc_kernel_map
, &addr
, size
)
379 return KERN_RESOURCE_SHORTAGE
;
383 * copy out the descriptors
385 bcopy((char *)&user_ldt
->ldt
[first_desc
],
389 simple_unlock(&pcb
->lock
);
392 vm_size_t size_used
, size_left
;
393 vm_map_copy_t memory
;
396 * Free any unused memory beyond the end of the last page used
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
);
404 * Zero the remainder of the page being returned.
406 size_left
= size_used
- ldt_size
;
408 bzero((char *)addr
+ ldt_size
, size_left
);
411 * Unwire the memory and make it into copyin form.
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
);
417 *desc_list
= (descriptor_list_t
) memory
;
429 user_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));