1 /*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or [email protected]
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56
57 /*
58 */
59
60 /*
61 * User LDT management.
62 * Each task may have its own LDT.
63 */
64
65 #include <kern/kalloc.h>
66 #include <kern/thread.h>
67 #include <kern/misc_protos.h>
68
69 #include <vm/vm_kern.h>
70
71 #include <i386/machdep_call.h>
72 #include <i386/user_ldt.h>
73 #include <i386/mp.h>
74 #include <i386/machine_routines.h>
75 #include <i386/proc_reg.h>
76 #include <i386/mp_desc.h>
77 #include <i386/seg.h>
78 #include <i386/thread.h>
79
80 #include <sys/errno.h>
81
82 static void user_ldt_set_action(void *);
83 static int i386_set_ldt_impl(uint32_t *retval, uint64_t start_sel, uint64_t descs,
84 uint64_t num_sels);
85 static int i386_get_ldt_impl(uint32_t *retval, uint64_t start_sel, uint64_t descs,
86 uint64_t num_sels);
87
88 #define USER_LDT_SIZE(descriptors) \
89 (sizeof(struct user_ldt) + (descriptors * sizeof(struct real_descriptor)))
90
91 /*
92 * Add the descriptors to the LDT, starting with
93 * the descriptor for 'first_selector'.
94 */
95
96 static int
i386_set_ldt_impl(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)97 i386_set_ldt_impl(
98 uint32_t *retval,
99 uint64_t start_sel,
100 uint64_t descs, /* out */
101 uint64_t num_sels)
102 {
103 user_ldt_t new_ldt, old_ldt;
104 struct real_descriptor *dp;
105 unsigned int i;
106 unsigned int min_selector = LDTSZ_MIN; /* do not allow the system selectors to be changed */
107 task_t task = current_task();
108 unsigned int ldt_count;
109 kern_return_t err;
110
111 if (start_sel != LDT_AUTO_ALLOC
112 && (start_sel != 0 || num_sels != 0)
113 && (start_sel < min_selector || start_sel >= LDTSZ || num_sels > LDTSZ)) {
114 return EINVAL;
115 }
116 if (start_sel != LDT_AUTO_ALLOC && start_sel + num_sels > LDTSZ) {
117 return EINVAL;
118 }
119
120 task_lock(task);
121
122 old_ldt = task->i386_ldt;
123
124 if (start_sel == LDT_AUTO_ALLOC) {
125 if (old_ldt) {
126 unsigned int null_count;
127 struct real_descriptor null_ldt;
128
129 bzero(&null_ldt, sizeof(null_ldt));
130
131 /*
132 * Look for null selectors among the already-allocated
133 * entries.
134 */
135 null_count = 0;
136 i = 0;
137 while (i < old_ldt->count) {
138 if (!memcmp(&old_ldt->ldt[i++], &null_ldt, sizeof(null_ldt))) {
139 null_count++;
140 if (null_count == num_sels) {
141 break; /* break out of while loop */
142 }
143 } else {
144 null_count = 0;
145 }
146 }
147
148 /*
149 * If we broke out of the while loop, i points to the selector
150 * after num_sels null selectors. Otherwise it points to the end
151 * of the old LDTs, and null_count is the number of null selectors
152 * at the end.
153 *
154 * Either way, there are null_count null selectors just prior to
155 * the i-indexed selector, and either null_count >= num_sels,
156 * or we're at the end, so we can extend.
157 */
158 start_sel = old_ldt->start + i - null_count;
159 } else {
160 start_sel = LDTSZ_MIN;
161 }
162
163 if (start_sel + num_sels > LDTSZ) {
164 task_unlock(task);
165 return ENOMEM;
166 }
167 }
168
169 if (start_sel == 0 && num_sels == 0) {
170 new_ldt = NULL;
171 } else {
172 /*
173 * Allocate new LDT
174 */
175
176 unsigned int begin_sel = (unsigned int)start_sel;
177 unsigned int end_sel = (unsigned int)begin_sel +
178 (unsigned int)num_sels;
179
180 if (old_ldt != NULL) {
181 if (old_ldt->start < begin_sel) {
182 begin_sel = old_ldt->start;
183 }
184 if (old_ldt->start + old_ldt->count > end_sel) {
185 end_sel = old_ldt->start + old_ldt->count;
186 }
187 }
188
189 ldt_count = end_sel - begin_sel;
190 /* XXX allocation under task lock */
191 new_ldt = (user_ldt_t)kalloc_data(USER_LDT_SIZE(ldt_count), Z_WAITOK);
192 if (new_ldt == NULL) {
193 task_unlock(task);
194 return ENOMEM;
195 }
196
197 new_ldt->start = begin_sel;
198 new_ldt->count = ldt_count;
199
200 /*
201 * Have new LDT. If there was a an old ldt, copy descriptors
202 * from old to new.
203 */
204 if (old_ldt) {
205 bcopy(&old_ldt->ldt[0],
206 &new_ldt->ldt[old_ldt->start - begin_sel],
207 old_ldt->count * sizeof(struct real_descriptor));
208
209 /*
210 * If the old and new LDTs are non-overlapping, fill the
211 * center in with null selectors.
212 */
213
214 if (old_ldt->start + old_ldt->count < start_sel) {
215 bzero(&new_ldt->ldt[old_ldt->count],
216 (start_sel - (old_ldt->start + old_ldt->count)) * sizeof(struct real_descriptor));
217 } else if (old_ldt->start > start_sel + num_sels) {
218 bzero(&new_ldt->ldt[num_sels],
219 (old_ldt->start - (start_sel + num_sels)) * sizeof(struct real_descriptor));
220 }
221 }
222
223 /*
224 * Install new descriptors.
225 */
226 if (descs != 0) {
227 /* XXX copyin under task lock */
228 err = copyin(descs, (char *)&new_ldt->ldt[start_sel - begin_sel],
229 num_sels * sizeof(struct real_descriptor));
230 if (err != 0) {
231 task_unlock(task);
232 user_ldt_free(new_ldt);
233 return err;
234 }
235 } else {
236 bzero(&new_ldt->ldt[start_sel - begin_sel], num_sels * sizeof(struct real_descriptor));
237 }
238 /*
239 * Validate descriptors.
240 * Only allow descriptors with user privileges.
241 */
242 for (i = 0, dp = (struct real_descriptor *) &new_ldt->ldt[start_sel - begin_sel];
243 i < num_sels;
244 i++, dp++) {
245 switch (dp->access & ~ACC_A) {
246 case 0:
247 case ACC_P:
248 /* valid empty descriptor, clear Present preemptively */
249 dp->access &= (~ACC_P & 0xff);
250 break;
251 case ACC_P | ACC_PL_U | ACC_DATA:
252 case ACC_P | ACC_PL_U | ACC_DATA_W:
253 case ACC_P | ACC_PL_U | ACC_DATA_E:
254 case ACC_P | ACC_PL_U | ACC_DATA_EW:
255 case ACC_P | ACC_PL_U | ACC_CODE:
256 case ACC_P | ACC_PL_U | ACC_CODE_R:
257 case ACC_P | ACC_PL_U | ACC_CODE_C:
258 case ACC_P | ACC_PL_U | ACC_CODE_CR:
259 break;
260 default:
261 task_unlock(task);
262 user_ldt_free(new_ldt);
263 return EACCES;
264 }
265 /* Reject attempts to create segments with 64-bit granules */
266 /* Note this restriction is still correct, even when
267 * executing as a 64-bit process (we want to maintain a single
268 * 64-bit selector (located in the GDT)).
269 */
270 if (dp->granularity & SZ_64) {
271 task_unlock(task);
272 user_ldt_free(new_ldt);
273 return EACCES;
274 }
275 }
276 }
277
278 task->i386_ldt = new_ldt; /* new LDT for task */
279
280 /*
281 * Switch to new LDT. We need to do this on all CPUs, since
282 * another thread in this same task may be currently running,
283 * and we need to make sure the new LDT is in place
284 * throughout the task before returning to the user.
285 */
286 mp_broadcast(user_ldt_set_action, task);
287
288 task_unlock(task);
289
290 /* free old LDT. We can't do this until after we've
291 * rendezvoused with all CPUs, in case another thread
292 * in this task was in the process of context switching.
293 */
294 if (old_ldt) {
295 user_ldt_free(old_ldt);
296 }
297
298 *retval = (uint32_t)start_sel;
299
300 return 0;
301 }
302
303 static int
i386_get_ldt_impl(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)304 i386_get_ldt_impl(
305 uint32_t *retval,
306 uint64_t start_sel,
307 uint64_t descs, /* out */
308 uint64_t num_sels)
309 {
310 user_ldt_t user_ldt;
311 task_t task = current_task();
312 unsigned int ldt_count;
313 kern_return_t err;
314
315 if (start_sel >= LDTSZ || num_sels > LDTSZ) {
316 return EINVAL;
317 }
318 if (start_sel + num_sels > LDTSZ) {
319 return EINVAL;
320 }
321 if (descs == 0) {
322 return EINVAL;
323 }
324
325 task_lock(task);
326
327 user_ldt = task->i386_ldt;
328 err = 0;
329
330 /*
331 * copy out the descriptors
332 */
333
334 if (user_ldt != 0) {
335 ldt_count = user_ldt->start + user_ldt->count;
336 } else {
337 ldt_count = LDTSZ_MIN;
338 }
339
340
341 if (start_sel < ldt_count) {
342 unsigned int copy_sels = (unsigned int)num_sels;
343
344 if (start_sel + num_sels > ldt_count) {
345 copy_sels = ldt_count - (unsigned int)start_sel;
346 }
347
348 err = copyout((char *)(current_ldt() + start_sel),
349 descs, copy_sels * sizeof(struct real_descriptor));
350 }
351
352 task_unlock(task);
353
354 *retval = ldt_count;
355
356 return err;
357 }
358
359 void
user_ldt_free(user_ldt_t user_ldt)360 user_ldt_free(
361 user_ldt_t user_ldt)
362 {
363 kfree_data(user_ldt, USER_LDT_SIZE(user_ldt->count));
364 }
365
366 user_ldt_t
user_ldt_copy(user_ldt_t user_ldt)367 user_ldt_copy(
368 user_ldt_t user_ldt)
369 {
370 if (user_ldt != NULL) {
371 size_t size = USER_LDT_SIZE(user_ldt->count);
372 user_ldt_t new_ldt = (user_ldt_t)kalloc_data(size, Z_WAITOK);
373 if (new_ldt != NULL) {
374 bcopy(user_ldt, new_ldt, size);
375 }
376 return new_ldt;
377 }
378
379 return 0;
380 }
381
382 void
user_ldt_set_action(void * arg)383 user_ldt_set_action(
384 void *arg)
385 {
386 task_t arg_task = (task_t)arg;
387
388 if (arg_task == current_task()) {
389 user_ldt_set(current_thread());
390 }
391 }
392
393 /*
394 * Set the LDT for the given thread on the current CPU. Should be invoked
395 * with interrupts disabled.
396 */
397 void
user_ldt_set(thread_t thread)398 user_ldt_set(
399 thread_t thread)
400 {
401 task_t task = get_threadtask(thread);
402 user_ldt_t user_ldt;
403
404 user_ldt = task->i386_ldt;
405
406 if (user_ldt != 0) {
407 struct real_descriptor *ldtp = current_ldt();
408
409 if (user_ldt->start > LDTSZ_MIN) {
410 bzero(&ldtp[LDTSZ_MIN],
411 sizeof(struct real_descriptor) * (user_ldt->start - LDTSZ_MIN));
412 }
413
414 bcopy(user_ldt->ldt, &ldtp[user_ldt->start],
415 sizeof(struct real_descriptor) * (user_ldt->count));
416
417 gdt_desc_p(USER_LDT)->limit_low = (uint16_t)((sizeof(struct real_descriptor) * (user_ldt->start + user_ldt->count)) - 1);
418
419 ml_cpu_set_ldt(USER_LDT);
420 } else {
421 ml_cpu_set_ldt(KERNEL_LDT);
422 }
423 }
424
425 /* For 32-bit processes, called via machdep_syscall() */
426 int
i386_set_ldt(uint32_t * retval,uint32_t start_sel,uint32_t descs,uint32_t num_sels)427 i386_set_ldt(
428 uint32_t *retval,
429 uint32_t start_sel,
430 uint32_t descs, /* out */
431 uint32_t num_sels)
432 {
433 return i386_set_ldt_impl(retval, (uint64_t)start_sel, (uint64_t)descs,
434 (uint64_t)num_sels);
435 }
436
437 /* For 64-bit processes, called via machdep_syscall64() */
438 int
i386_set_ldt64(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)439 i386_set_ldt64(
440 uint32_t *retval,
441 uint64_t start_sel,
442 uint64_t descs, /* out */
443 uint64_t num_sels)
444 {
445 return i386_set_ldt_impl(retval, start_sel, descs, num_sels);
446 }
447
448 /* For 32-bit processes, called via machdep_syscall() */
449 int
i386_get_ldt(uint32_t * retval,uint32_t start_sel,uint32_t descs,uint32_t num_sels)450 i386_get_ldt(
451 uint32_t *retval,
452 uint32_t start_sel,
453 uint32_t descs, /* out */
454 uint32_t num_sels)
455 {
456 return i386_get_ldt_impl(retval, (uint64_t)start_sel, (uint64_t)descs,
457 (uint64_t)num_sels);
458 }
459
460 /* For 64-bit processes, called via machdep_syscall64() */
461 int
i386_get_ldt64(uint32_t * retval,uint64_t start_sel,uint64_t descs,uint64_t num_sels)462 i386_get_ldt64(
463 uint32_t *retval,
464 uint64_t start_sel,
465 uint64_t descs, /* out */
466 uint64_t num_sels)
467 {
468 return i386_get_ldt_impl(retval, start_sel, descs, num_sels);
469 }
470