]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/user_ldt.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / osfmk / i386 / user_ldt.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
8ad349bb 4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
1c79356b 5 *
8ad349bb
A
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
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
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.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
1c79356b
A
29 */
30/*
31 * @OSF_COPYRIGHT@
32 */
33/*
34 * Mach Operating System
35 * Copyright (c) 1991 Carnegie Mellon University
36 * All Rights Reserved.
37 *
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.
43 *
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.
47 *
48 * Carnegie Mellon requests users of this software to return to
49 *
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
54 *
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
57 */
58
59/*
60 */
61
62/*
63 * User LDT management.
5d5c5d0d 64 * Each task may have its own LDT.
1c79356b
A
65 */
66
67#include <kern/kalloc.h>
68#include <kern/thread.h>
69#include <kern/misc_protos.h>
70
71#include <vm/vm_kern.h>
72
73#include <i386/seg.h>
74#include <i386/thread.h>
75#include <i386/user_ldt.h>
5d5c5d0d
A
76#include <i386/mp_desc.h>
77#include <i386/proc_reg.h>
78#include <i386/machdep_call.h>
79#include <i386/mp.h>
80#include <i386/machine_routines.h>
1c79356b 81
5d5c5d0d 82#include <sys/errno.h>
8ad349bb 83
5d5c5d0d 84static void user_ldt_set_action(void *);
1c79356b
A
85
86/*
87 * Add the descriptors to the LDT, starting with
88 * the descriptor for 'first_selector'.
89 */
90
5d5c5d0d 91int
1c79356b 92i386_set_ldt(
5d5c5d0d
A
93 int *retval,
94 uint32_t start_sel,
95 uint32_t descs, /* out */
96 uint32_t num_sels)
1c79356b 97{
5d5c5d0d 98 user_ldt_t new_ldt, old_ldt;
1c79356b 99 struct real_descriptor *dp;
5d5c5d0d
A
100 unsigned int i;
101 unsigned int min_selector = LDTSZ_MIN; /* do not allow the system selectors to be changed */
102 task_t task = current_task();
103 unsigned int ldt_count;
104 kern_return_t err;
c0fea474 105
5d5c5d0d
A
106 if (start_sel != LDT_AUTO_ALLOC
107 && (start_sel != 0 || num_sels != 0)
108 && (start_sel < min_selector || start_sel >= LDTSZ))
109 return EINVAL;
110 if (start_sel != LDT_AUTO_ALLOC
111 && start_sel + num_sels > LDTSZ)
112 return EINVAL;
c0fea474 113
5d5c5d0d
A
114 task_lock(task);
115
116 old_ldt = task->i386_ldt;
117
118 if (start_sel == LDT_AUTO_ALLOC) {
119 if (old_ldt) {
120 unsigned int null_count;
121 struct real_descriptor null_ldt;
122
123 bzero(&null_ldt, sizeof(null_ldt));
124
125 /*
126 * Look for null selectors among the already-allocated
127 * entries.
128 */
129 null_count = 0;
130 i = 0;
131 while (i < old_ldt->count)
132 {
133 if (!memcmp(&old_ldt->ldt[i++], &null_ldt, sizeof(null_ldt))) {
134 null_count++;
135 if (null_count == num_sels)
136 break; /* break out of while loop */
137 } else {
138 null_count = 0;
139 }
140 }
141
142 /*
143 * If we broke out of the while loop, i points to the selector
144 * after num_sels null selectors. Otherwise it points to the end
145 * of the old LDTs, and null_count is the number of null selectors
146 * at the end.
147 *
148 * Either way, there are null_count null selectors just prior to
149 * the i-indexed selector, and either null_count >= num_sels,
150 * or we're at the end, so we can extend.
151 */
152 start_sel = old_ldt->start + i - null_count;
153 } else {
154 start_sel = LDTSZ_MIN;
155 }
156
157 if (start_sel + num_sels > LDTSZ) {
158 task_unlock(task);
159 return ENOMEM;
1c79356b
A
160 }
161 }
5d5c5d0d
A
162
163 if (start_sel == 0 && num_sels == 0) {
164 new_ldt = NULL;
165 } else {
1c79356b 166 /*
5d5c5d0d 167 * Allocate new LDT
1c79356b 168 */
5d5c5d0d
A
169
170 unsigned int begin_sel = start_sel;
171 unsigned int end_sel = begin_sel + num_sels;
172
173 if (old_ldt != NULL) {
174 if (old_ldt->start < begin_sel)
175 begin_sel = old_ldt->start;
176 if (old_ldt->start + old_ldt->count > end_sel)
177 end_sel = old_ldt->start + old_ldt->count;
178 }
179
180 ldt_count = end_sel - begin_sel;
181
182 new_ldt = (user_ldt_t)kalloc(sizeof(struct user_ldt) + (ldt_count * sizeof(struct real_descriptor)));
183 if (new_ldt == NULL) {
184 task_unlock(task);
185 return ENOMEM;
1c79356b
A
186 }
187
5d5c5d0d
A
188 new_ldt->start = begin_sel;
189 new_ldt->count = ldt_count;
190
1c79356b
A
191 /*
192 * Have new LDT. If there was a an old ldt, copy descriptors
5d5c5d0d 193 * from old to new.
1c79356b
A
194 */
195 if (old_ldt) {
5d5c5d0d
A
196 bcopy(&old_ldt->ldt[0],
197 &new_ldt->ldt[old_ldt->start - begin_sel],
198 old_ldt->count * sizeof(struct real_descriptor));
199
200 /*
201 * If the old and new LDTs are non-overlapping, fill the
202 * center in with null selectors.
203 */
204
205 if (old_ldt->start + old_ldt->count < start_sel)
206 bzero(&new_ldt->ldt[old_ldt->count],
207 (start_sel - (old_ldt->start + old_ldt->count)) * sizeof(struct real_descriptor));
208 else if (old_ldt->start > start_sel + num_sels)
209 bzero(&new_ldt->ldt[num_sels],
210 (old_ldt->start - (start_sel + num_sels)) * sizeof(struct real_descriptor));
1c79356b 211 }
5d5c5d0d
A
212
213 /*
214 * Install new descriptors.
215 */
216 if (descs != 0) {
217 err = copyin(descs, (char *)&new_ldt->ldt[start_sel - begin_sel],
218 num_sels * sizeof(struct real_descriptor));
219 if (err != 0)
220 {
221 task_unlock(task);
222 user_ldt_free(new_ldt);
223 return err;
1c79356b 224 }
5d5c5d0d
A
225 } else {
226 bzero(&new_ldt->ldt[start_sel - begin_sel], num_sels * sizeof(struct real_descriptor));
1c79356b
A
227 }
228
5d5c5d0d
A
229 /*
230 * Validate descriptors.
231 * Only allow descriptors with user priviledges.
232 */
233 for (i = 0, dp = (struct real_descriptor *) &new_ldt->ldt[start_sel - begin_sel];
234 i < num_sels;
235 i++, dp++)
236 {
237 switch (dp->access & ~ACC_A) {
238 case 0:
239 case ACC_P:
240 /* valid empty descriptor */
241 break;
242 case ACC_P | ACC_PL_U | ACC_DATA:
243 case ACC_P | ACC_PL_U | ACC_DATA_W:
244 case ACC_P | ACC_PL_U | ACC_DATA_E:
245 case ACC_P | ACC_PL_U | ACC_DATA_EW:
246 case ACC_P | ACC_PL_U | ACC_CODE:
247 case ACC_P | ACC_PL_U | ACC_CODE_R:
248 case ACC_P | ACC_PL_U | ACC_CODE_C:
249 case ACC_P | ACC_PL_U | ACC_CODE_CR:
250 case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
251 case ACC_P | ACC_PL_U | ACC_CALL_GATE:
252 break;
253 default:
254 task_unlock(task);
255 user_ldt_free(new_ldt);
256 return EACCES;
257 }
258 }
8ad349bb 259 }
c0fea474 260
5d5c5d0d
A
261 task->i386_ldt = new_ldt; /* new LDT for task */
262
1c79356b 263 /*
5d5c5d0d
A
264 * Switch to new LDT. We need to do this on all CPUs, since
265 * another thread in this same task may be currently running,
266 * and we need to make sure the new LDT is in place
267 * throughout the task before returning to the user.
1c79356b 268 */
5d5c5d0d 269 mp_rendezvous_no_intrs(user_ldt_set_action, task);
1c79356b 270
5d5c5d0d 271 task_unlock(task);
1c79356b 272
5d5c5d0d
A
273 /* free old LDT. We can't do this until after we've
274 * rendezvoused with all CPUs, in case another thread
275 * in this task was in the process of context switching.
8ad349bb 276 */
5d5c5d0d
A
277 if (old_ldt)
278 user_ldt_free(old_ldt);
279
280 *retval = start_sel;
281
282 return 0;
1c79356b
A
283}
284
5d5c5d0d 285int
1c79356b 286i386_get_ldt(
5d5c5d0d
A
287 int *retval,
288 uint32_t start_sel,
289 uint32_t descs, /* out */
290 uint32_t num_sels)
1c79356b 291{
5d5c5d0d
A
292 user_ldt_t user_ldt;
293 task_t task = current_task();
1c79356b 294 unsigned int ldt_count;
5d5c5d0d 295 kern_return_t err;
1c79356b 296
5d5c5d0d
A
297 if (start_sel >= 8192)
298 return EINVAL;
299 if (start_sel + num_sels > 8192)
300 return EINVAL;
301 if (descs == 0)
302 return EINVAL;
1c79356b 303
5d5c5d0d 304 task_lock(task);
1c79356b 305
5d5c5d0d
A
306 user_ldt = task->i386_ldt;
307 err = 0;
c0fea474 308
5d5c5d0d
A
309 /*
310 * copy out the descriptors
311 */
312
313 if (user_ldt != 0)
314 ldt_count = user_ldt->start + user_ldt->count;
315 else
316 ldt_count = LDTSZ_MIN;
1c79356b 317
1c79356b 318
5d5c5d0d
A
319 if (start_sel < ldt_count)
320 {
321 unsigned int copy_sels = num_sels;
1c79356b 322
5d5c5d0d
A
323 if (start_sel + num_sels > ldt_count)
324 copy_sels = ldt_count - start_sel;
1c79356b 325
5d5c5d0d
A
326 err = copyout((char *)(current_ldt() + start_sel),
327 descs, copy_sels * sizeof(struct real_descriptor));
1c79356b
A
328 }
329
5d5c5d0d 330 task_unlock(task);
1c79356b 331
5d5c5d0d 332 *retval = ldt_count;
1c79356b 333
5d5c5d0d
A
334 return err;
335}
1c79356b 336
5d5c5d0d
A
337void
338user_ldt_free(
339 user_ldt_t user_ldt)
340{
341 kfree(user_ldt, sizeof(struct user_ldt) + (user_ldt->count * sizeof(struct real_descriptor)));
342}
1c79356b 343
5d5c5d0d
A
344user_ldt_t
345user_ldt_copy(
346 user_ldt_t user_ldt)
347{
348 if (user_ldt != NULL) {
349 size_t size = sizeof(struct user_ldt) + (user_ldt->count * sizeof(struct real_descriptor));
350 user_ldt_t new_ldt = (user_ldt_t)kalloc(size);
351 if (new_ldt != NULL)
352 bcopy(user_ldt, new_ldt, size);
353 return new_ldt;
1c79356b 354 }
5d5c5d0d
A
355
356 return 0;
357}
1c79356b 358
5d5c5d0d
A
359void
360user_ldt_set_action(
361 void *arg)
362{
363 task_t arg_task = (task_t)arg;
364
365 if (arg_task == current_task()) {
366 user_ldt_set(current_thread());
367 }
1c79356b
A
368}
369
5d5c5d0d
A
370/*
371 * Set the LDT for the given thread on the current CPU. Should be invoked
372 * with interrupts disabled.
373 */
1c79356b 374void
5d5c5d0d
A
375user_ldt_set(
376 thread_t thread)
1c79356b 377{
5d5c5d0d
A
378 task_t task = thread->task;
379 user_ldt_t user_ldt;
380
381 user_ldt = task->i386_ldt;
382
383 if (user_ldt != 0) {
384 struct real_descriptor *ldtp = (struct real_descriptor *)current_ldt();
385
386 if (user_ldt->start > LDTSZ_MIN) {
387 bzero(&ldtp[LDTSZ_MIN],
388 sizeof(struct real_descriptor) * (user_ldt->start - LDTSZ_MIN));
389 }
390
391 bcopy(user_ldt->ldt, &ldtp[user_ldt->start],
392 sizeof(struct real_descriptor) * (user_ldt->count));
393
394 gdt_desc_p(USER_LDT)->limit_low = (sizeof(struct real_descriptor) * (user_ldt->start + user_ldt->count)) - 1;
395
396 ml_cpu_set_ldt(USER_LDT);
397 } else {
398 ml_cpu_set_ldt(KERNEL_LDT);
399 }
1c79356b 400}