]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/sys_machdep.c
This commit was generated by cvs2svn to compensate for changes in r168609,
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / sys_machdep.c
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *      from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_kstack_pages.h"
36 #include "opt_mac.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/mutex.h>
43 #include <sys/priv.h>
44 #include <sys/proc.h>
45 #include <sys/smp.h>
46 #include <sys/sysproto.h>
47
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50 #include <vm/vm_map.h>
51 #include <vm/vm_extern.h>
52
53 #include <machine/cpu.h>
54 #include <machine/pcb.h>
55 #include <machine/pcb_ext.h>
56 #include <machine/proc.h>
57 #include <machine/sysarch.h>
58
59 #include <security/audit/audit.h>
60 #include <security/mac/mac_framework.h>
61
62 #include <vm/vm_kern.h>         /* for kernel_map */
63
64 #define MAX_LD 8192
65 #define LD_PER_PAGE 512
66 #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
67 #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
68
69
70
71 static int i386_set_ldt_data(struct thread *, int start, int num,
72         union descriptor *descs);
73 static int i386_ldt_grow(struct thread *td, int len);
74 #ifdef SMP
75 static void set_user_ldt_rv(struct thread *);
76 #endif
77
78 #ifndef _SYS_SYSPROTO_H_
79 struct sysarch_args {
80         int op;
81         char *parms;
82 };
83 #endif
84
85 int
86 sysarch(td, uap)
87         struct thread *td;
88         register struct sysarch_args *uap;
89 {
90         int error;
91         union descriptor *lp;
92         union {
93                 struct i386_ldt_args largs;
94                 struct i386_ioperm_args iargs;
95         } kargs;
96         uint32_t base;
97         struct segment_descriptor sd, *sdp;
98
99         AUDIT_ARG(cmd, uap->op);
100         switch (uap->op) {
101         case I386_GET_IOPERM:
102         case I386_SET_IOPERM:
103                 if ((error = copyin(uap->parms, &kargs.iargs,
104                     sizeof(struct i386_ioperm_args))) != 0)
105                         return (error);
106                 break;
107         case I386_GET_LDT:
108         case I386_SET_LDT:
109                 if ((error = copyin(uap->parms, &kargs.largs,
110                     sizeof(struct i386_ldt_args))) != 0)
111                         return (error);
112                 if (kargs.largs.num > MAX_LD || kargs.largs.num <= 0)
113                         return (EINVAL);
114                 break;
115         default:
116                 break;
117         }
118
119         mtx_lock(&Giant);
120         switch(uap->op) {
121         case I386_GET_LDT:
122                 error = i386_get_ldt(td, &kargs.largs);
123                 break;
124         case I386_SET_LDT:
125                 if (kargs.largs.descs != NULL) {
126                         lp = (union descriptor *)kmem_alloc(kernel_map,
127                             kargs.largs.num * sizeof(union descriptor));
128                         if (lp == NULL) {
129                                 error = ENOMEM;
130                                 break;
131                         }
132                         error = copyin(kargs.largs.descs, lp,
133                             kargs.largs.num * sizeof(union descriptor));
134                         if (error == 0)
135                                 error = i386_set_ldt(td, &kargs.largs, lp);
136                         kmem_free(kernel_map, (vm_offset_t)lp,
137                             kargs.largs.num * sizeof(union descriptor));
138                 } else {
139                         error = i386_set_ldt(td, &kargs.largs, NULL);
140                 }
141                 break;
142         case I386_GET_IOPERM:
143                 error = i386_get_ioperm(td, &kargs.iargs);
144                 if (error == 0)
145                         error = copyout(&kargs.iargs, uap->parms,
146                             sizeof(struct i386_ioperm_args));
147                 break;
148         case I386_SET_IOPERM:
149                 error = i386_set_ioperm(td, &kargs.iargs);
150                 break;
151         case I386_VM86:
152                 error = vm86_sysarch(td, uap->parms);
153                 break;
154         case I386_GET_FSBASE:
155                 sdp = &td->td_pcb->pcb_fsd;
156                 base = sdp->sd_hibase << 24 | sdp->sd_lobase;
157                 error = copyout(&base, uap->parms, sizeof(base));
158                 break;
159         case I386_SET_FSBASE:
160                 error = copyin(uap->parms, &base, sizeof(base));
161                 if (!error) {
162                         /*
163                          * Construct a descriptor and store it in the pcb for
164                          * the next context switch.  Also store it in the gdt
165                          * so that the load of tf_fs into %fs will activate it
166                          * at return to userland.
167                          */
168                         sd.sd_lobase = base & 0xffffff;
169                         sd.sd_hibase = (base >> 24) & 0xff;
170                         sd.sd_lolimit = 0xffff; /* 4GB limit, wraps around */
171                         sd.sd_hilimit = 0xf;
172                         sd.sd_type  = SDT_MEMRWA;
173                         sd.sd_dpl   = SEL_UPL;
174                         sd.sd_p     = 1;
175                         sd.sd_xx    = 0;
176                         sd.sd_def32 = 1;
177                         sd.sd_gran  = 1;
178                         critical_enter();
179                         td->td_pcb->pcb_fsd = sd;
180                         PCPU_GET(fsgs_gdt)[0] = sd;
181                         critical_exit();
182                         td->td_frame->tf_fs = GSEL(GUFS_SEL, SEL_UPL);
183                 }
184                 break;
185         case I386_GET_GSBASE:
186                 sdp = &td->td_pcb->pcb_gsd;
187                 base = sdp->sd_hibase << 24 | sdp->sd_lobase;
188                 error = copyout(&base, uap->parms, sizeof(base));
189                 break;
190         case I386_SET_GSBASE:
191                 error = copyin(uap->parms, &base, sizeof(base));
192                 if (!error) {
193                         /*
194                          * Construct a descriptor and store it in the pcb for
195                          * the next context switch.  Also store it in the gdt
196                          * because we have to do a load_gs() right now.
197                          */
198                         sd.sd_lobase = base & 0xffffff;
199                         sd.sd_hibase = (base >> 24) & 0xff;
200                         sd.sd_lolimit = 0xffff; /* 4GB limit, wraps around */
201                         sd.sd_hilimit = 0xf;
202                         sd.sd_type  = SDT_MEMRWA;
203                         sd.sd_dpl   = SEL_UPL;
204                         sd.sd_p     = 1;
205                         sd.sd_xx    = 0;
206                         sd.sd_def32 = 1;
207                         sd.sd_gran  = 1;
208                         critical_enter();
209                         td->td_pcb->pcb_gsd = sd;
210                         PCPU_GET(fsgs_gdt)[1] = sd;
211                         critical_exit();
212                         load_gs(GSEL(GUGS_SEL, SEL_UPL));
213                 }
214                 break;
215         default:
216                 error = EINVAL;
217                 break;
218         }
219         mtx_unlock(&Giant);
220         return (error);
221 }
222
223 int
224 i386_extend_pcb(struct thread *td)
225 {
226         int i, offset;
227         u_long *addr;
228         struct pcb_ext *ext;
229         struct soft_segment_descriptor ssd = {
230                 0,                      /* segment base address (overwritten) */
231                 ctob(IOPAGES + 1) - 1,  /* length */
232                 SDT_SYS386TSS,          /* segment type */
233                 0,                      /* priority level */
234                 1,                      /* descriptor present */
235                 0, 0,
236                 0,                      /* default 32 size */
237                 0                       /* granularity */
238         };
239
240         if (td->td_proc->p_flag & P_SA)
241                 return (EINVAL);                /* XXXKSE */
242 /* XXXKSE  All the code below only works in 1:1   needs changing */
243         ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
244         if (ext == 0)
245                 return (ENOMEM);
246         bzero(ext, sizeof(struct pcb_ext)); 
247         /* -16 is so we can convert a trapframe into vm86trapframe inplace */
248         ext->ext_tss.tss_esp0 = td->td_kstack + ctob(KSTACK_PAGES) -
249             sizeof(struct pcb) - 16;
250         ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
251         /*
252          * The last byte of the i/o map must be followed by an 0xff byte.
253          * We arbitrarily allocate 16 bytes here, to keep the starting
254          * address on a doubleword boundary.
255          */
256         offset = PAGE_SIZE - 16;
257         ext->ext_tss.tss_ioopt = 
258             (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
259         ext->ext_iomap = (caddr_t)ext + offset;
260         ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
261
262         addr = (u_long *)ext->ext_vm86.vm86_intmap;
263         for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
264                 *addr++ = ~0;
265
266         ssd.ssd_base = (unsigned)&ext->ext_tss;
267         ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
268         ssdtosd(&ssd, &ext->ext_tssd);
269
270         KASSERT(td == curthread, ("giving TSS to !curthread"));
271         KASSERT(td->td_pcb->pcb_ext == 0, ("already have a TSS!"));
272
273         /* Switch to the new TSS. */
274         critical_enter();
275         td->td_pcb->pcb_ext = ext;
276         PCPU_SET(private_tss, 1);
277         *PCPU_GET(tss_gdt) = ext->ext_tssd;
278         ltr(GSEL(GPROC0_SEL, SEL_KPL));
279         critical_exit();
280
281         return 0;
282 }
283
284 int
285 i386_set_ioperm(td, uap)
286         struct thread *td;
287         struct i386_ioperm_args *uap;
288 {
289         int i, error;
290         char *iomap;
291
292 #ifdef MAC
293         if ((error = mac_check_sysarch_ioperm(td->td_ucred)) != 0)
294                 return (error);
295 #endif
296         if ((error = priv_check(td, PRIV_IO)) != 0)
297                 return (error);
298         if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
299                 return (error);
300         /*
301          * XXX 
302          * While this is restricted to root, we should probably figure out
303          * whether any other driver is using this i/o address, as so not to
304          * cause confusion.  This probably requires a global 'usage registry'.
305          */
306
307         if (td->td_pcb->pcb_ext == 0)
308                 if ((error = i386_extend_pcb(td)) != 0)
309                         return (error);
310         iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
311
312         if (uap->start + uap->length > IOPAGES * PAGE_SIZE * NBBY)
313                 return (EINVAL);
314
315         for (i = uap->start; i < uap->start + uap->length; i++) {
316                 if (uap->enable)
317                         iomap[i >> 3] &= ~(1 << (i & 7));
318                 else
319                         iomap[i >> 3] |= (1 << (i & 7));
320         }
321         return (error);
322 }
323
324 int
325 i386_get_ioperm(td, uap)
326         struct thread *td;
327         struct i386_ioperm_args *uap;
328 {
329         int i, state;
330         char *iomap;
331
332         if (uap->start >= IOPAGES * PAGE_SIZE * NBBY)
333                 return (EINVAL);
334
335         if (td->td_pcb->pcb_ext == 0) {
336                 uap->length = 0;
337                 goto done;
338         }
339
340         iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
341
342         i = uap->start;
343         state = (iomap[i >> 3] >> (i & 7)) & 1;
344         uap->enable = !state;
345         uap->length = 1;
346
347         for (i = uap->start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) {
348                 if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
349                         break;
350                 uap->length++;
351         }
352
353 done:
354         return (0);
355 }
356
357 /*
358  * Update the GDT entry pointing to the LDT to point to the LDT of the
359  * current process.
360  *
361  * This must be called with sched_lock held.  Unfortunately, we can't use a
362  * mtx_assert() here because cpu_switch() calls this function after changing
363  * curproc but before sched_lock's owner is updated in mi_switch().
364  */   
365 void
366 set_user_ldt(struct mdproc *mdp)
367 {
368         struct proc_ldt *pldt;
369
370         pldt = mdp->md_ldt;
371 #ifdef SMP
372         gdt[PCPU_GET(cpuid) * NGDT + GUSERLDT_SEL].sd = pldt->ldt_sd;
373 #else
374         gdt[GUSERLDT_SEL].sd = pldt->ldt_sd;
375 #endif
376         lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
377         PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL));
378 }
379
380 #ifdef SMP
381 static void
382 set_user_ldt_rv(struct thread *td)
383 {
384
385         if (td->td_proc != curthread->td_proc)
386                 return;
387
388         set_user_ldt(&td->td_proc->p_md);
389 }
390 #endif
391
392 /*
393  * Must be called with either sched_lock free or held but not recursed.
394  * If it does not return NULL, it will return with it owned.
395  */
396 struct proc_ldt *
397 user_ldt_alloc(struct mdproc *mdp, int len)
398 {
399         struct proc_ldt *pldt, *new_ldt;
400
401         if (mtx_owned(&sched_lock))
402                 mtx_unlock_spin(&sched_lock);
403         mtx_assert(&sched_lock, MA_NOTOWNED);
404         MALLOC(new_ldt, struct proc_ldt *, sizeof(struct proc_ldt),
405                 M_SUBPROC, M_WAITOK);
406
407         new_ldt->ldt_len = len = NEW_MAX_LD(len);
408         new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
409                 len * sizeof(union descriptor));
410         if (new_ldt->ldt_base == NULL) {
411                 FREE(new_ldt, M_SUBPROC);
412                 return NULL;
413         }
414         new_ldt->ldt_refcnt = 1;
415         new_ldt->ldt_active = 0;
416
417         mtx_lock_spin(&sched_lock);
418         gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
419         gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
420         ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
421
422         if ((pldt = mdp->md_ldt)) {
423                 if (len > pldt->ldt_len)
424                         len = pldt->ldt_len;
425                 bcopy(pldt->ldt_base, new_ldt->ldt_base,
426                     len * sizeof(union descriptor));
427         } else {
428                 bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
429         }
430         return new_ldt;
431 }
432
433 /*
434  * Must be called either with sched_lock free or held but not recursed.
435  * If md_ldt is not NULL, it will return with sched_lock released.
436  */
437 void
438 user_ldt_free(struct thread *td)
439 {
440         struct mdproc *mdp = &td->td_proc->p_md;
441         struct proc_ldt *pldt = mdp->md_ldt;
442
443         if (pldt == NULL)
444                 return;
445
446         if (!mtx_owned(&sched_lock))
447                 mtx_lock_spin(&sched_lock);
448         mtx_assert(&sched_lock, MA_OWNED | MA_NOTRECURSED);
449         if (td == PCPU_GET(curthread)) {
450                 lldt(_default_ldt);
451                 PCPU_SET(currentldt, _default_ldt);
452         }
453
454         mdp->md_ldt = NULL;
455         if (--pldt->ldt_refcnt == 0) {
456                 mtx_unlock_spin(&sched_lock);
457                 kmem_free(kernel_map, (vm_offset_t)pldt->ldt_base,
458                         pldt->ldt_len * sizeof(union descriptor));
459                 FREE(pldt, M_SUBPROC);
460         } else
461                 mtx_unlock_spin(&sched_lock);
462 }
463
464 /*
465  * Note for the authors of compat layers (linux, etc): copyout() in
466  * the function below is not a problem since it presents data in
467  * arch-specific format (i.e. i386-specific in this case), not in
468  * the OS-specific one.
469  */
470 int
471 i386_get_ldt(td, uap)
472         struct thread *td;
473         struct i386_ldt_args *uap;
474 {
475         int error = 0;
476         struct proc_ldt *pldt = td->td_proc->p_md.md_ldt;
477         int nldt, num;
478         union descriptor *lp;
479
480 #ifdef  DEBUG
481         printf("i386_get_ldt: start=%d num=%d descs=%p\n",
482             uap->start, uap->num, (void *)uap->descs);
483 #endif
484
485         if (pldt) {
486                 nldt = pldt->ldt_len;
487                 num = min(uap->num, nldt);
488                 lp = &((union descriptor *)(pldt->ldt_base))[uap->start];
489         } else {
490                 nldt = sizeof(ldt)/sizeof(ldt[0]);
491                 num = min(uap->num, nldt);
492                 lp = &ldt[uap->start];
493         }
494
495         if ((uap->start > (unsigned int)nldt) ||
496             ((unsigned int)num > (unsigned int)nldt) ||
497             ((unsigned int)(uap->start + num) > (unsigned int)nldt))
498                 return(EINVAL);
499
500         error = copyout(lp, uap->descs, num * sizeof(union descriptor));
501         if (!error)
502                 td->td_retval[0] = num;
503
504         return(error);
505 }
506
507 static int ldt_warnings;
508 #define NUM_LDT_WARNINGS 10
509
510 int
511 i386_set_ldt(td, uap, descs)
512         struct thread *td;
513         struct i386_ldt_args *uap;
514         union descriptor *descs;
515 {
516         int error = 0, i;
517         int largest_ld;
518         struct mdproc *mdp = &td->td_proc->p_md;
519         struct proc_ldt *pldt;
520         union descriptor *dp;
521
522 #ifdef  DEBUG
523         printf("i386_set_ldt: start=%d num=%d descs=%p\n",
524             uap->start, uap->num, (void *)uap->descs);
525 #endif
526
527         if (descs == NULL) {
528                 /* Free descriptors */
529                 if (uap->start == 0 && uap->num == 0) {
530                         /*
531                          * Treat this as a special case, so userland needn't
532                          * know magic number NLDT.
533                          */
534                         uap->start = NLDT;
535                         uap->num = MAX_LD - NLDT;
536                 }
537                 if (uap->num <= 0)
538                         return (EINVAL);
539                 mtx_lock_spin(&sched_lock);
540                 pldt = mdp->md_ldt;
541                 if (pldt == NULL || uap->start >= pldt->ldt_len) {
542                         mtx_unlock_spin(&sched_lock);
543                         return (0);
544                 }
545                 largest_ld = uap->start + uap->num;
546                 if (largest_ld > pldt->ldt_len)
547                         largest_ld = pldt->ldt_len;
548                 i = largest_ld - uap->start;
549                 bzero(&((union descriptor *)(pldt->ldt_base))[uap->start],
550                     sizeof(union descriptor) * i);
551                 mtx_unlock_spin(&sched_lock);
552                 return (0);
553         }
554
555         if (!(uap->start == LDT_AUTO_ALLOC && uap->num == 1)) {
556                 /* complain a for a while if using old methods */
557                 if (ldt_warnings++ < NUM_LDT_WARNINGS) {
558                         printf("Warning: pid %d used static ldt allocation.\n",
559                             td->td_proc->p_pid);
560                         printf("See the i386_set_ldt man page for more info\n");
561                 }
562                 /* verify range of descriptors to modify */
563                 largest_ld = uap->start + uap->num;
564                 if (uap->start >= MAX_LD ||
565                     uap->num < 0 || largest_ld > MAX_LD) {
566                         return (EINVAL);
567                 }
568         }
569
570         /* Check descriptors for access violations */
571         for (i = 0; i < uap->num; i++) {
572                 dp = &descs[i];
573
574                 switch (dp->sd.sd_type) {
575                 case SDT_SYSNULL:       /* system null */ 
576                         dp->sd.sd_p = 0;
577                         break;
578                 case SDT_SYS286TSS: /* system 286 TSS available */
579                 case SDT_SYSLDT:    /* system local descriptor table */
580                 case SDT_SYS286BSY: /* system 286 TSS busy */
581                 case SDT_SYSTASKGT: /* system task gate */
582                 case SDT_SYS286IGT: /* system 286 interrupt gate */
583                 case SDT_SYS286TGT: /* system 286 trap gate */
584                 case SDT_SYSNULL2:  /* undefined by Intel */ 
585                 case SDT_SYS386TSS: /* system 386 TSS available */
586                 case SDT_SYSNULL3:  /* undefined by Intel */
587                 case SDT_SYS386BSY: /* system 386 TSS busy */
588                 case SDT_SYSNULL4:  /* undefined by Intel */ 
589                 case SDT_SYS386IGT: /* system 386 interrupt gate */
590                 case SDT_SYS386TGT: /* system 386 trap gate */
591                 case SDT_SYS286CGT: /* system 286 call gate */ 
592                 case SDT_SYS386CGT: /* system 386 call gate */
593                         /* I can't think of any reason to allow a user proc
594                          * to create a segment of these types.  They are
595                          * for OS use only.
596                          */
597                         return (EACCES);
598                         /*NOTREACHED*/
599
600                 /* memory segment types */
601                 case SDT_MEMEC:   /* memory execute only conforming */
602                 case SDT_MEMEAC:  /* memory execute only accessed conforming */
603                 case SDT_MEMERC:  /* memory execute read conforming */
604                 case SDT_MEMERAC: /* memory execute read accessed conforming */
605                          /* Must be "present" if executable and conforming. */
606                         if (dp->sd.sd_p == 0)
607                                 return (EACCES);
608                         break;
609                 case SDT_MEMRO:   /* memory read only */
610                 case SDT_MEMROA:  /* memory read only accessed */
611                 case SDT_MEMRW:   /* memory read write */
612                 case SDT_MEMRWA:  /* memory read write accessed */
613                 case SDT_MEMROD:  /* memory read only expand dwn limit */
614                 case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
615                 case SDT_MEMRWD:  /* memory read write expand dwn limit */  
616                 case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
617                 case SDT_MEME:    /* memory execute only */ 
618                 case SDT_MEMEA:   /* memory execute only accessed */
619                 case SDT_MEMER:   /* memory execute read */
620                 case SDT_MEMERA:  /* memory execute read accessed */
621                         break;
622                 default:
623                         return(EINVAL);
624                         /*NOTREACHED*/
625                 }
626
627                 /* Only user (ring-3) descriptors may be present. */
628                 if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL))
629                         return (EACCES);
630         }
631
632         if (uap->start == LDT_AUTO_ALLOC && uap->num == 1) {
633                 /* Allocate a free slot */
634                 pldt = mdp->md_ldt;
635                 if (pldt == NULL) {
636                         error = i386_ldt_grow(td, NLDT + 1);
637                         if (error)
638                                 return (error);
639                         pldt = mdp->md_ldt;
640                 }
641 again:
642                 mtx_lock_spin(&sched_lock);
643                 /*
644                  * start scanning a bit up to leave room for NVidia and
645                  * Wine, which still user the "Blat" method of allocation.
646                  */
647                 dp = &((union descriptor *)(pldt->ldt_base))[NLDT];
648                 for (i = NLDT; i < pldt->ldt_len; ++i) {
649                         if (dp->sd.sd_type == SDT_SYSNULL)
650                                 break;
651                         dp++;
652                 }
653                 if (i >= pldt->ldt_len) {
654                         mtx_unlock_spin(&sched_lock);
655                         error = i386_ldt_grow(td, pldt->ldt_len+1);
656                         if (error)
657                                 return (error);
658                         goto again;
659                 }
660                 uap->start = i;
661                 error = i386_set_ldt_data(td, i, 1, descs);
662                 mtx_unlock_spin(&sched_lock);
663         } else {
664                 largest_ld = uap->start + uap->num;
665                 error = i386_ldt_grow(td, largest_ld);
666                 if (error == 0) {
667                         mtx_lock_spin(&sched_lock);
668                         error = i386_set_ldt_data(td, uap->start, uap->num,
669                             descs);
670                         mtx_unlock_spin(&sched_lock);
671                 }
672         }
673         if (error == 0)
674                 td->td_retval[0] = uap->start;
675         return (error);
676 }
677
678 static int
679 i386_set_ldt_data(struct thread *td, int start, int num,
680         union descriptor *descs)
681 {
682         struct mdproc *mdp = &td->td_proc->p_md;
683         struct proc_ldt *pldt = mdp->md_ldt;
684
685         mtx_assert(&sched_lock, MA_OWNED);
686
687         /* Fill in range */
688         bcopy(descs,
689             &((union descriptor *)(pldt->ldt_base))[start],
690             num * sizeof(union descriptor));
691         return (0);
692 }
693
694 static int
695 i386_ldt_grow(struct thread *td, int len) 
696 {
697         struct mdproc *mdp = &td->td_proc->p_md;
698         struct proc_ldt *pldt;
699         caddr_t old_ldt_base;
700         int old_ldt_len;
701
702         if (len > MAX_LD)
703                 return (ENOMEM);
704         if (len < NLDT + 1)
705                 len = NLDT + 1;
706
707         /* Allocate a user ldt. */
708         pldt = mdp->md_ldt;
709         if (!pldt || len > pldt->ldt_len) {
710                 struct proc_ldt *new_ldt;
711
712                 new_ldt = user_ldt_alloc(mdp, len);
713                 if (new_ldt == NULL)
714                         return (ENOMEM);
715                 pldt = mdp->md_ldt;
716
717                 /* sched_lock was acquired by user_ldt_alloc. */
718                 if (pldt) {
719                         if (new_ldt->ldt_len > pldt->ldt_len) {
720                                 old_ldt_base = pldt->ldt_base;
721                                 old_ldt_len = pldt->ldt_len;
722                                 pldt->ldt_sd = new_ldt->ldt_sd;
723                                 pldt->ldt_base = new_ldt->ldt_base;
724                                 pldt->ldt_len = new_ldt->ldt_len;
725                                 mtx_unlock_spin(&sched_lock);
726                                 kmem_free(kernel_map, (vm_offset_t)old_ldt_base,
727                                         old_ldt_len * sizeof(union descriptor));
728                                 FREE(new_ldt, M_SUBPROC);
729                                 mtx_lock_spin(&sched_lock);
730                         } else {
731                                 /*
732                                  * If other threads already did the work,
733                                  * do nothing.
734                                  */
735                                 mtx_unlock_spin(&sched_lock);
736                                 kmem_free(kernel_map,
737                                    (vm_offset_t)new_ldt->ldt_base,
738                                    new_ldt->ldt_len * sizeof(union descriptor));
739                                 FREE(new_ldt, M_SUBPROC);
740                                 return (0);
741                         }
742                 } else {
743                         mdp->md_ldt = pldt = new_ldt;
744                 }
745 #ifdef SMP
746                 mtx_unlock_spin(&sched_lock);
747                 /* signal other cpus to reload ldt */
748                 smp_rendezvous(NULL, (void (*)(void *))set_user_ldt_rv,
749                     NULL, td);
750 #else
751                 set_user_ldt(mdp);
752                 mtx_unlock_spin(&sched_lock);
753 #endif
754         }
755         return (0);
756 }