2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
29 * Mach Operating System
30 * Copyright (c) 1991 Carnegie Mellon University
31 * All Rights Reserved.
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43 * Carnegie Mellon requests users of this software to return to
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
58 * User LDT management.
59 * Each thread in a task may have its own LDT.
62 #include <kern/kalloc.h>
63 #include <kern/thread.h>
64 #include <kern/misc_protos.h>
66 #include <vm/vm_kern.h>
69 #include <i386/thread.h>
70 #include <i386/user_ldt.h>
72 char acc_type
[8][3] = {
74 { 0, 0, 1 }, /* data */
75 { 0, 1, 1 }, /* data, writable */
76 { 0, 0, 1 }, /* data, expand-down */
77 { 0, 1, 1 }, /* data, writable, expand-down */
78 { 1, 0, 0 }, /* code */
79 { 1, 0, 1 }, /* code, readable */
80 { 1, 0, 0 }, /* code, conforming */
81 { 1, 0, 1 }, /* code, readable, conforming */
84 extern struct fake_descriptor ldt
[]; /* for system call gate */
89 extern boolean_t
selector_check(
100 struct user_ldt
*ldt
;
103 ldt
= thread
->top_act
->mact
.pcb
->ims
.ldt
;
107 return sel
== USER_CS
;
109 return sel
== USER_DS
;
117 if (type
!= S_DATA
&& sel
== 0)
119 if ((sel
& (SEL_LDTS
|SEL_PL
)) != (SEL_LDTS
|SEL_PL_U
)
120 || sel
> ldt
->desc
.limit_low
)
123 access
= ldt
->ldt
[sel_idx(sel
)].access
;
125 if ((access
& (ACC_P
|ACC_PL
|ACC_TYPE_USER
))
126 != (ACC_P
|ACC_PL_U
|ACC_TYPE_USER
))
128 /* present, pl == pl.user, not system */
130 return acc_type
[(access
& 0xe)>>1][type
];
134 * Add the descriptors to the LDT, starting with
135 * the descriptor for 'first_selector'.
140 thread_act_t thr_act
,
142 descriptor_list_t desc_list
,
143 mach_msg_type_number_t count
)
145 user_ldt_t new_ldt
, old_ldt
, temp
;
146 struct real_descriptor
*dp
;
148 int min_selector
= 0;
150 vm_size_t ldt_size_needed
;
151 int first_desc
= sel_idx(first_selector
);
152 vm_map_copy_t old_copy_object
;
155 if (first_desc
< min_selector
|| first_desc
> 8191)
156 return KERN_INVALID_ARGUMENT
;
157 if (first_desc
+ count
>= 8192)
158 return KERN_INVALID_ARGUMENT
;
159 if (thr_act
== THR_ACT_NULL
)
160 return KERN_INVALID_ARGUMENT
;
161 if ((thread
= act_lock_thread(thr_act
)) == THREAD_NULL
) {
162 act_unlock_thread(thr_act
);
163 return KERN_INVALID_ARGUMENT
;
165 if (thread
== current_thread())
166 min_selector
= LDTSZ
;
167 act_unlock_thread(thr_act
);
170 * We must copy out desc_list to the kernel map, and wire
171 * it down (we touch it while the PCB is locked).
173 * We make a copy of the copyin object, and clear
174 * out the old one, so that the MIG stub will have a
175 * a empty (but valid) copyin object to discard.
179 vm_offset_t dst_addr
;
181 old_copy_object
= (vm_map_copy_t
) desc_list
;
183 kr
= vm_map_copyout(ipc_kernel_map
, &dst_addr
,
184 vm_map_copy_copy(old_copy_object
));
185 if (kr
!= KERN_SUCCESS
)
188 (void) vm_map_wire(ipc_kernel_map
,
189 trunc_page(dst_addr
),
190 round_page(dst_addr
+
191 count
* sizeof(struct real_descriptor
)),
192 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
193 desc_list
= (descriptor_list_t
) dst_addr
;
196 for (i
= 0, dp
= (struct real_descriptor
*) desc_list
;
200 switch (dp
->access
& ~ACC_A
) {
203 /* valid empty descriptor */
205 case ACC_P
| ACC_CALL_GATE
:
206 /* Mach kernel call */
207 *dp
= *(struct real_descriptor
*)
208 &ldt
[sel_idx(USER_SCALL
)];
210 case ACC_P
| ACC_PL_U
| ACC_DATA
:
211 case ACC_P
| ACC_PL_U
| ACC_DATA_W
:
212 case ACC_P
| ACC_PL_U
| ACC_DATA_E
:
213 case ACC_P
| ACC_PL_U
| ACC_DATA_EW
:
214 case ACC_P
| ACC_PL_U
| ACC_CODE
:
215 case ACC_P
| ACC_PL_U
| ACC_CODE_R
:
216 case ACC_P
| ACC_PL_U
| ACC_CODE_C
:
217 case ACC_P
| ACC_PL_U
| ACC_CODE_CR
:
218 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE_16
:
219 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE
:
222 (void) vm_map_remove(ipc_kernel_map
,
223 (vm_offset_t
) desc_list
,
224 count
* sizeof(struct real_descriptor
),
225 VM_MAP_REMOVE_KUNWIRE
);
226 return KERN_INVALID_ARGUMENT
;
229 ldt_size_needed
= sizeof(struct real_descriptor
)
230 * (first_desc
+ count
);
232 pcb
= thr_act
->mact
.pcb
;
235 simple_lock(&pcb
->lock
);
236 old_ldt
= pcb
->ims
.ldt
;
238 old_ldt
->desc
.limit_low
+ 1 < ldt_size_needed
)
241 * No old LDT, or not big enough
244 simple_unlock(&pcb
->lock
);
246 new_ldt
= (user_ldt_t
) kalloc(ldt_size_needed
247 + sizeof(struct real_descriptor
));
248 new_ldt
->desc
.limit_low
= ldt_size_needed
- 1;
249 new_ldt
->desc
.limit_high
= 0;
250 new_ldt
->desc
.base_low
=
251 ((vm_offset_t
)&new_ldt
->ldt
[0]) & 0xffff;
252 new_ldt
->desc
.base_med
=
253 (((vm_offset_t
)&new_ldt
->ldt
[0]) >> 16)
255 new_ldt
->desc
.base_high
=
256 ((vm_offset_t
)&new_ldt
->ldt
[0]) >> 24;
257 new_ldt
->desc
.access
= ACC_P
| ACC_LDT
;
258 new_ldt
->desc
.granularity
= 0;
264 * Have new LDT. If there was a an old ldt, copy descriptors
265 * from old to new. Otherwise copy the default ldt.
268 bcopy((char *)&old_ldt
->ldt
[0],
269 (char *)&new_ldt
->ldt
[0],
270 old_ldt
->desc
.limit_low
+ 1);
272 else if (thr_act
== current_act()) {
273 struct real_descriptor
template = {0, 0, 0, ACC_P
, 0, 0 ,0};
275 for (dp
= &new_ldt
->ldt
[0], i
= 0; i
< first_desc
; i
++, dp
++) {
277 *dp
= *(struct real_descriptor
*) &ldt
[i
];
284 old_ldt
= new_ldt
; /* use new LDT from now on */
285 new_ldt
= temp
; /* discard old LDT */
287 pcb
->ims
.ldt
= old_ldt
; /* new LDT for thread */
291 * Install new descriptors.
293 bcopy((char *)desc_list
,
294 (char *)&old_ldt
->ldt
[first_desc
],
295 count
* sizeof(struct real_descriptor
));
297 simple_unlock(&pcb
->lock
);
300 kfree((vm_offset_t
)new_ldt
,
301 new_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));
304 * Free the descriptor list.
306 (void) vm_map_remove(ipc_kernel_map
, (vm_offset_t
) desc_list
,
307 count
* sizeof(struct real_descriptor
),
308 VM_MAP_REMOVE_KUNWIRE
);
314 thread_act_t thr_act
,
316 int selector_count
, /* number wanted */
317 descriptor_list_t
*desc_list
, /* in/out */
318 mach_msg_type_number_t
*count
) /* in/out */
320 struct user_ldt
*user_ldt
;
321 pcb_t pcb
= thr_act
->mact
.pcb
;
322 int first_desc
= sel_idx(first_selector
);
323 unsigned int ldt_count
;
325 vm_size_t size
, size_needed
;
329 if (thr_act
== THR_ACT_NULL
|| (thread
= thr_act
->thread
)==THREAD_NULL
)
330 return KERN_INVALID_ARGUMENT
;
332 if (first_desc
< 0 || first_desc
> 8191)
333 return KERN_INVALID_ARGUMENT
;
334 if (first_desc
+ selector_count
>= 8192)
335 return KERN_INVALID_ARGUMENT
;
341 simple_lock(&pcb
->lock
);
342 user_ldt
= pcb
->ims
.ldt
;
344 simple_unlock(&pcb
->lock
);
346 kmem_free(ipc_kernel_map
, addr
, size
);
352 * Find how many descriptors we should return.
354 ldt_count
= (user_ldt
->desc
.limit_low
+ 1) /
355 sizeof (struct real_descriptor
);
356 ldt_count
-= first_desc
;
357 if (ldt_count
> selector_count
)
358 ldt_count
= selector_count
;
360 ldt_size
= ldt_count
* sizeof(struct real_descriptor
);
363 * Do we have the memory we need?
365 if (ldt_count
<= *count
)
366 break; /* fits in-line */
368 size_needed
= round_page(ldt_size
);
369 if (size_needed
<= size
)
373 * Unlock the pcb and allocate more memory
375 simple_unlock(&pcb
->lock
);
378 kmem_free(ipc_kernel_map
, addr
, size
);
382 if (kmem_alloc(ipc_kernel_map
, &addr
, size
)
384 return KERN_RESOURCE_SHORTAGE
;
388 * copy out the descriptors
390 bcopy((char *)&user_ldt
->ldt
[first_desc
],
394 simple_unlock(&pcb
->lock
);
397 vm_size_t size_used
, size_left
;
398 vm_map_copy_t memory
;
401 * Free any unused memory beyond the end of the last page used
403 size_used
= round_page(ldt_size
);
404 if (size_used
!= size
)
405 kmem_free(ipc_kernel_map
,
406 addr
+ size_used
, size
- size_used
);
409 * Zero the remainder of the page being returned.
411 size_left
= size_used
- ldt_size
;
413 bzero((char *)addr
+ ldt_size
, size_left
);
416 * Unwire the memory and make it into copyin form.
418 (void) vm_map_unwire(ipc_kernel_map
, trunc_page(addr
),
419 round_page(addr
+ size_used
), FALSE
);
420 (void) vm_map_copyin(ipc_kernel_map
, addr
, size_used
,
422 *desc_list
= (descriptor_list_t
) memory
;
433 kfree((vm_offset_t
)user_ldt
,
434 user_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));