2 * Copyright (c) 2000 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 */
81 extern struct fake_descriptor ldt
[]; /* for system call gate */
86 extern boolean_t
selector_check(
100 ldt
= thread
->top_act
->mact
.pcb
->ims
.ldt
;
104 return sel
== USER_CS
;
106 return sel
== USER_DS
;
114 if (type
!= S_DATA
&& sel
== 0)
116 if ((sel
& (SEL_LDTS
|SEL_PL
)) != (SEL_LDTS
|SEL_PL_U
)
117 || sel
> ldt
->desc
.limit_low
)
120 access
= ldt
->ldt
[sel_idx(sel
)].access
;
122 if ((access
& (ACC_P
|ACC_PL
|ACC_TYPE_USER
))
123 != (ACC_P
|ACC_PL_U
|ACC_TYPE_USER
))
125 /* present, pl == pl.user, not system */
127 return acc_type
[(access
& 0xe)>>1][type
];
131 * Add the descriptors to the LDT, starting with
132 * the descriptor for 'first_selector'.
137 thread_act_t thr_act
,
139 descriptor_list_t desc_list
,
140 mach_msg_type_number_t count
)
142 user_ldt_t new_ldt
, old_ldt
, temp
;
143 struct real_descriptor
*dp
;
145 int min_selector
= 0;
147 vm_size_t ldt_size_needed
;
148 int first_desc
= sel_idx(first_selector
);
149 vm_map_copy_t old_copy_object
;
152 if (first_desc
< min_selector
|| first_desc
> 8191)
153 return KERN_INVALID_ARGUMENT
;
154 if (first_desc
+ count
>= 8192)
155 return KERN_INVALID_ARGUMENT
;
156 if (thr_act
== THR_ACT_NULL
)
157 return KERN_INVALID_ARGUMENT
;
158 if ((thread
= act_lock_thread(thr_act
)) == THREAD_NULL
) {
159 act_unlock_thread(thr_act
);
160 return KERN_INVALID_ARGUMENT
;
162 if (thread
== current_thread())
163 min_selector
= LDTSZ
;
164 act_unlock_thread(thr_act
);
167 * We must copy out desc_list to the kernel map, and wire
168 * it down (we touch it while the PCB is locked).
170 * We make a copy of the copyin object, and clear
171 * out the old one, so that the MIG stub will have a
172 * a empty (but valid) copyin object to discard.
176 vm_offset_t dst_addr
;
178 old_copy_object
= (vm_map_copy_t
) desc_list
;
180 kr
= vm_map_copyout(ipc_kernel_map
, &dst_addr
,
181 vm_map_copy_copy(old_copy_object
));
182 if (kr
!= KERN_SUCCESS
)
185 (void) vm_map_wire(ipc_kernel_map
,
186 trunc_page(dst_addr
),
187 round_page(dst_addr
+
188 count
* sizeof(struct real_descriptor
)),
189 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
190 desc_list
= (descriptor_list_t
) dst_addr
;
193 for (i
= 0, dp
= (struct real_descriptor
*) desc_list
;
197 switch (dp
->access
& ~ACC_A
) {
200 /* valid empty descriptor */
202 case ACC_P
| ACC_CALL_GATE
:
203 /* Mach kernel call */
204 *dp
= *(struct real_descriptor
*)
205 &ldt
[sel_idx(USER_SCALL
)];
207 case ACC_P
| ACC_PL_U
| ACC_DATA
:
208 case ACC_P
| ACC_PL_U
| ACC_DATA_W
:
209 case ACC_P
| ACC_PL_U
| ACC_DATA_E
:
210 case ACC_P
| ACC_PL_U
| ACC_DATA_EW
:
211 case ACC_P
| ACC_PL_U
| ACC_CODE
:
212 case ACC_P
| ACC_PL_U
| ACC_CODE_R
:
213 case ACC_P
| ACC_PL_U
| ACC_CODE_C
:
214 case ACC_P
| ACC_PL_U
| ACC_CODE_CR
:
215 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE_16
:
216 case ACC_P
| ACC_PL_U
| ACC_CALL_GATE
:
219 (void) vm_map_remove(ipc_kernel_map
,
220 (vm_offset_t
) desc_list
,
221 count
* sizeof(struct real_descriptor
),
222 VM_MAP_REMOVE_KUNWIRE
);
223 return KERN_INVALID_ARGUMENT
;
226 ldt_size_needed
= sizeof(struct real_descriptor
)
227 * (first_desc
+ count
);
229 pcb
= thr_act
->mact
.pcb
;
232 simple_lock(&pcb
->lock
);
233 old_ldt
= pcb
->ims
.ldt
;
235 old_ldt
->desc
.limit_low
+ 1 < ldt_size_needed
)
238 * No old LDT, or not big enough
241 simple_unlock(&pcb
->lock
);
243 new_ldt
= (user_ldt_t
) kalloc(ldt_size_needed
244 + sizeof(struct real_descriptor
));
245 new_ldt
->desc
.limit_low
= ldt_size_needed
- 1;
246 new_ldt
->desc
.limit_high
= 0;
247 new_ldt
->desc
.base_low
=
248 ((vm_offset_t
)&new_ldt
->ldt
[0]) & 0xffff;
249 new_ldt
->desc
.base_med
=
250 (((vm_offset_t
)&new_ldt
->ldt
[0]) >> 16)
252 new_ldt
->desc
.base_high
=
253 ((vm_offset_t
)&new_ldt
->ldt
[0]) >> 24;
254 new_ldt
->desc
.access
= ACC_P
| ACC_LDT
;
255 new_ldt
->desc
.granularity
= 0;
261 * Have new LDT. If there was a an old ldt, copy descriptors
262 * from old to new. Otherwise copy the default ldt.
265 bcopy((char *)&old_ldt
->ldt
[0],
266 (char *)&new_ldt
->ldt
[0],
267 old_ldt
->desc
.limit_low
+ 1);
269 else if (thr_act
== current_act()) {
270 struct real_descriptor
template = {0, 0, 0, ACC_P
, 0, 0 ,0};
272 for (dp
= &new_ldt
->ldt
[0], i
= 0; i
< first_desc
; i
++, dp
++) {
274 *dp
= *(struct real_descriptor
*) &ldt
[i
];
281 old_ldt
= new_ldt
; /* use new LDT from now on */
282 new_ldt
= temp
; /* discard old LDT */
284 pcb
->ims
.ldt
= old_ldt
; /* new LDT for thread */
288 * Install new descriptors.
290 bcopy((char *)desc_list
,
291 (char *)&old_ldt
->ldt
[first_desc
],
292 count
* sizeof(struct real_descriptor
));
294 simple_unlock(&pcb
->lock
);
297 kfree((vm_offset_t
)new_ldt
,
298 new_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));
301 * Free the descriptor list.
303 (void) vm_map_remove(ipc_kernel_map
, (vm_offset_t
) desc_list
,
304 count
* sizeof(struct real_descriptor
),
305 VM_MAP_REMOVE_KUNWIRE
);
311 thread_act_t thr_act
,
313 int selector_count
, /* number wanted */
314 descriptor_list_t
*desc_list
, /* in/out */
315 mach_msg_type_number_t
*count
) /* in/out */
317 struct user_ldt
*user_ldt
;
318 pcb_t pcb
= thr_act
->mact
.pcb
;
319 int first_desc
= sel_idx(first_selector
);
320 unsigned int ldt_count
;
322 vm_size_t size
, size_needed
;
326 if (thr_act
== THR_ACT_NULL
|| (thread
= thr_act
->thread
)==THREAD_NULL
)
327 return KERN_INVALID_ARGUMENT
;
329 if (first_desc
< 0 || first_desc
> 8191)
330 return KERN_INVALID_ARGUMENT
;
331 if (first_desc
+ selector_count
>= 8192)
332 return KERN_INVALID_ARGUMENT
;
338 simple_lock(&pcb
->lock
);
339 user_ldt
= pcb
->ims
.ldt
;
341 simple_unlock(&pcb
->lock
);
343 kmem_free(ipc_kernel_map
, addr
, size
);
349 * Find how many descriptors we should return.
351 ldt_count
= (user_ldt
->desc
.limit_low
+ 1) /
352 sizeof (struct real_descriptor
);
353 ldt_count
-= first_desc
;
354 if (ldt_count
> selector_count
)
355 ldt_count
= selector_count
;
357 ldt_size
= ldt_count
* sizeof(struct real_descriptor
);
360 * Do we have the memory we need?
362 if (ldt_count
<= *count
)
363 break; /* fits in-line */
365 size_needed
= round_page(ldt_size
);
366 if (size_needed
<= size
)
370 * Unlock the pcb and allocate more memory
372 simple_unlock(&pcb
->lock
);
375 kmem_free(ipc_kernel_map
, addr
, size
);
379 if (kmem_alloc(ipc_kernel_map
, &addr
, size
)
381 return KERN_RESOURCE_SHORTAGE
;
385 * copy out the descriptors
387 bcopy((char *)&user_ldt
->ldt
[first_desc
],
391 simple_unlock(&pcb
->lock
);
394 vm_size_t size_used
, size_left
;
395 vm_map_copy_t memory
;
398 * Free any unused memory beyond the end of the last page used
400 size_used
= round_page(ldt_size
);
401 if (size_used
!= size
)
402 kmem_free(ipc_kernel_map
,
403 addr
+ size_used
, size
- size_used
);
406 * Zero the remainder of the page being returned.
408 size_left
= size_used
- ldt_size
;
410 bzero((char *)addr
+ ldt_size
, size_left
);
413 * Unwire the memory and make it into copyin form.
415 (void) vm_map_unwire(ipc_kernel_map
, trunc_page(addr
),
416 round_page(addr
+ size_used
), FALSE
);
417 (void) vm_map_copyin(ipc_kernel_map
, addr
, size_used
,
419 *desc_list
= (descriptor_list_t
) memory
;
430 kfree((vm_offset_t
)user_ldt
,
431 user_ldt
->desc
.limit_low
+1+sizeof(struct real_descriptor
));