xref: /freebsd/sys/i386/i386/sys_machdep.c (revision e4e2c613)
15b81b6b3SRodney W. Grimes /*-
25b81b6b3SRodney W. Grimes  * Copyright (c) 1990 The Regents of the University of California.
35b81b6b3SRodney W. Grimes  * All rights reserved.
45b81b6b3SRodney W. Grimes  *
55b81b6b3SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
65b81b6b3SRodney W. Grimes  * modification, are permitted provided that the following conditions
75b81b6b3SRodney W. Grimes  * are met:
85b81b6b3SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
95b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
105b81b6b3SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
115b81b6b3SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
125b81b6b3SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
135b81b6b3SRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
145b81b6b3SRodney W. Grimes  *    must display the following acknowledgement:
155b81b6b3SRodney W. Grimes  *	This product includes software developed by the University of
165b81b6b3SRodney W. Grimes  *	California, Berkeley and its contributors.
175b81b6b3SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
185b81b6b3SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
195b81b6b3SRodney W. Grimes  *    without specific prior written permission.
205b81b6b3SRodney W. Grimes  *
215b81b6b3SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
225b81b6b3SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
235b81b6b3SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
245b81b6b3SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
255b81b6b3SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
265b81b6b3SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
275b81b6b3SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
285b81b6b3SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
295b81b6b3SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
305b81b6b3SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
315b81b6b3SRodney W. Grimes  * SUCH DAMAGE.
325b81b6b3SRodney W. Grimes  *
3347cacd38SRodney W. Grimes  *	from: @(#)sys_machdep.c	5.5 (Berkeley) 1/19/91
34812a11a2SDavid Greenman  *
355b81b6b3SRodney W. Grimes  */
365b81b6b3SRodney W. Grimes 
379676a785SDavid E. O'Brien #include <sys/cdefs.h>
389676a785SDavid E. O'Brien __FBSDID("$FreeBSD$");
399676a785SDavid E. O'Brien 
40b40ce416SJulian Elischer #include "opt_kstack_pages.h"
4192835789SRobert Watson #include "opt_mac.h"
42268bdb43SPeter Wemm 
43f540b106SGarrett Wollman #include <sys/param.h>
44f540b106SGarrett Wollman #include <sys/systm.h>
45fb919e4dSMark Murray #include <sys/lock.h>
4692835789SRobert Watson #include <sys/mac.h>
4791c28bfdSLuoqi Chen #include <sys/malloc.h>
48fb919e4dSMark Murray #include <sys/mutex.h>
49f540b106SGarrett Wollman #include <sys/proc.h>
506caa8a15SJohn Baldwin #include <sys/smp.h>
51fb919e4dSMark Murray #include <sys/sysproto.h>
52fb919e4dSMark Murray #include <sys/user.h>
53efeaf95aSDavid Greenman 
54efeaf95aSDavid Greenman #include <vm/vm.h>
55efeaf95aSDavid Greenman #include <vm/pmap.h>
56efeaf95aSDavid Greenman #include <vm/vm_map.h>
57efeaf95aSDavid Greenman #include <vm/vm_extern.h>
58efeaf95aSDavid Greenman 
59f540b106SGarrett Wollman #include <machine/cpu.h>
60b67dffdaSPeter Wemm #include <machine/pcb_ext.h>	/* pcb.h included by sys/user.h */
6124db0459SJohn Baldwin #include <machine/proc.h>
62f540b106SGarrett Wollman #include <machine/sysarch.h>
63812a11a2SDavid Greenman 
64f540b106SGarrett Wollman #include <vm/vm_kern.h>		/* for kernel_map */
65da59a31cSDavid Greenman 
660dbf6d73SJordan K. Hubbard #define MAX_LD 8192
670dbf6d73SJordan K. Hubbard #define LD_PER_PAGE 512
680dbf6d73SJordan K. Hubbard #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
690dbf6d73SJordan K. Hubbard #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
700dbf6d73SJordan K. Hubbard 
710dbf6d73SJordan K. Hubbard 
720dbf6d73SJordan K. Hubbard 
7315fe3067SAlfred Perlstein static int i386_get_ldt(struct thread *, char *);
7415fe3067SAlfred Perlstein static int i386_set_ldt(struct thread *, char *);
75dae8d52dSDavid Xu static int i386_set_ldt_data(struct thread *, int start, int num,
76dae8d52dSDavid Xu 	union descriptor *descs);
77dae8d52dSDavid Xu static int i386_ldt_grow(struct thread *td, int len);
7815fe3067SAlfred Perlstein static int i386_get_ioperm(struct thread *, char *);
7915fe3067SAlfred Perlstein static int i386_set_ioperm(struct thread *, char *);
805eb6e279SJohn Baldwin #ifdef SMP
8115fe3067SAlfred Perlstein static void set_user_ldt_rv(struct thread *);
825eb6e279SJohn Baldwin #endif
835b81b6b3SRodney W. Grimes 
842f1ba63bSBruce Evans #ifndef _SYS_SYSPROTO_H_
85812a11a2SDavid Greenman struct sysarch_args {
86812a11a2SDavid Greenman 	int op;
87812a11a2SDavid Greenman 	char *parms;
8801ae5b20SDavid Greenman };
892f1ba63bSBruce Evans #endif
9001ae5b20SDavid Greenman 
91812a11a2SDavid Greenman int
92b40ce416SJulian Elischer sysarch(td, uap)
93b40ce416SJulian Elischer 	struct thread *td;
94812a11a2SDavid Greenman 	register struct sysarch_args *uap;
955b81b6b3SRodney W. Grimes {
967ff022c4SJohn Baldwin 	int error;
975b81b6b3SRodney W. Grimes 
987ff022c4SJohn Baldwin 	mtx_lock(&Giant);
99812a11a2SDavid Greenman 	switch(uap->op) {
100812a11a2SDavid Greenman 	case I386_GET_LDT:
101b40ce416SJulian Elischer 		error = i386_get_ldt(td, uap->parms);
1025b81b6b3SRodney W. Grimes 		break;
1035b81b6b3SRodney W. Grimes 
104812a11a2SDavid Greenman 	case I386_SET_LDT:
105b40ce416SJulian Elischer 		error = i386_set_ldt(td, uap->parms);
1065b81b6b3SRodney W. Grimes 		break;
10748a09cf2SJohn Dyson 	case I386_GET_IOPERM:
108b40ce416SJulian Elischer 		error = i386_get_ioperm(td, uap->parms);
10948a09cf2SJohn Dyson 		break;
11048a09cf2SJohn Dyson 	case I386_SET_IOPERM:
111b40ce416SJulian Elischer 		error = i386_set_ioperm(td, uap->parms);
11248a09cf2SJohn Dyson 		break;
11348a09cf2SJohn Dyson 	case I386_VM86:
114b40ce416SJulian Elischer 		error = vm86_sysarch(td, uap->parms);
11548a09cf2SJohn Dyson 		break;
116812a11a2SDavid Greenman 	default:
1177ff022c4SJohn Baldwin 		error = EINVAL;
118812a11a2SDavid Greenman 		break;
119812a11a2SDavid Greenman 	}
1207ff022c4SJohn Baldwin 	mtx_unlock(&Giant);
121812a11a2SDavid Greenman 	return (error);
122812a11a2SDavid Greenman }
123da59a31cSDavid Greenman 
12448a09cf2SJohn Dyson int
125b40ce416SJulian Elischer i386_extend_pcb(struct thread *td)
12648a09cf2SJohn Dyson {
12748a09cf2SJohn Dyson 	int i, offset;
12848a09cf2SJohn Dyson 	u_long *addr;
12948a09cf2SJohn Dyson 	struct pcb_ext *ext;
13048a09cf2SJohn Dyson 	struct soft_segment_descriptor ssd = {
13148a09cf2SJohn Dyson 		0,			/* segment base address (overwritten) */
13248a09cf2SJohn Dyson 		ctob(IOPAGES + 1) - 1,	/* length */
13348a09cf2SJohn Dyson 		SDT_SYS386TSS,		/* segment type */
13448a09cf2SJohn Dyson 		0,			/* priority level */
13548a09cf2SJohn Dyson 		1,			/* descriptor present */
13648a09cf2SJohn Dyson 		0, 0,
13748a09cf2SJohn Dyson 		0,			/* default 32 size */
13848a09cf2SJohn Dyson 		0			/* granularity */
13948a09cf2SJohn Dyson 	};
14048a09cf2SJohn Dyson 
1410e2a4d3aSDavid Xu 	if (td->td_proc->p_flag & P_SA)
142b40ce416SJulian Elischer 		return (EINVAL);		/* XXXKSE */
143b40ce416SJulian Elischer /* XXXKSE  All the code below only works in 1:1   needs changing */
14448a09cf2SJohn Dyson 	ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
14548a09cf2SJohn Dyson 	if (ext == 0)
14648a09cf2SJohn Dyson 		return (ENOMEM);
147640c4313SJonathan Lemon 	bzero(ext, sizeof(struct pcb_ext));
148b40ce416SJulian Elischer 	/* -16 is so we can convert a trapframe into vm86trapframe inplace */
149b40ce416SJulian Elischer 	ext->ext_tss.tss_esp0 = td->td_kstack + ctob(KSTACK_PAGES) -
150b40ce416SJulian Elischer 	    sizeof(struct pcb) - 16;
15148a09cf2SJohn Dyson 	ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
15248a09cf2SJohn Dyson 	/*
15348a09cf2SJohn Dyson 	 * The last byte of the i/o map must be followed by an 0xff byte.
15448a09cf2SJohn Dyson 	 * We arbitrarily allocate 16 bytes here, to keep the starting
15548a09cf2SJohn Dyson 	 * address on a doubleword boundary.
15648a09cf2SJohn Dyson 	 */
15748a09cf2SJohn Dyson 	offset = PAGE_SIZE - 16;
15848a09cf2SJohn Dyson 	ext->ext_tss.tss_ioopt =
15948a09cf2SJohn Dyson 	    (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
16048a09cf2SJohn Dyson 	ext->ext_iomap = (caddr_t)ext + offset;
16148a09cf2SJohn Dyson 	ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
16248a09cf2SJohn Dyson 
16348a09cf2SJohn Dyson 	addr = (u_long *)ext->ext_vm86.vm86_intmap;
16448a09cf2SJohn Dyson 	for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
16548a09cf2SJohn Dyson 		*addr++ = ~0;
16648a09cf2SJohn Dyson 
16748a09cf2SJohn Dyson 	ssd.ssd_base = (unsigned)&ext->ext_tss;
16848a09cf2SJohn Dyson 	ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
16948a09cf2SJohn Dyson 	ssdtosd(&ssd, &ext->ext_tssd);
17048a09cf2SJohn Dyson 
1715e0f6bc4SPeter Wemm 	KASSERT(td->td_proc == curthread->td_proc, ("giving TSS to !curproc"));
172b40ce416SJulian Elischer 	KASSERT(td->td_pcb->pcb_ext == 0, ("already have a TSS!"));
1736caa8a15SJohn Baldwin 	mtx_lock_spin(&sched_lock);
174b40ce416SJulian Elischer 	td->td_pcb->pcb_ext = ext;
175df4d012bSJohn Baldwin 
176df4d012bSJohn Baldwin 	/* switch to the new TSS after syscall completes */
1774a338afdSJulian Elischer 	td->td_flags |= TDF_NEEDRESCHED;
1786caa8a15SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
17948a09cf2SJohn Dyson 
18048a09cf2SJohn Dyson 	return 0;
18148a09cf2SJohn Dyson }
18248a09cf2SJohn Dyson 
18348a09cf2SJohn Dyson static int
184b40ce416SJulian Elischer i386_set_ioperm(td, args)
185b40ce416SJulian Elischer 	struct thread *td;
18648a09cf2SJohn Dyson 	char *args;
18748a09cf2SJohn Dyson {
1889729f279SBruce Evans 	int i, error;
18948a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
19048a09cf2SJohn Dyson 	char *iomap;
19148a09cf2SJohn Dyson 
1920a5e03ddSMatthew Dillon 	if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
19348a09cf2SJohn Dyson 		return (error);
19448a09cf2SJohn Dyson 
19592835789SRobert Watson #ifdef MAC
19692835789SRobert Watson 	if ((error = mac_check_sysarch_ioperm(td->td_ucred)) != 0)
19792835789SRobert Watson 		return (error);
19892835789SRobert Watson #endif
19944731cabSJohn Baldwin 	if ((error = suser(td)) != 0)
20048a09cf2SJohn Dyson 		return (error);
201a854ed98SJohn Baldwin 	if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
202330e7889SRobert Watson 		return (error);
20348a09cf2SJohn Dyson 	/*
20448a09cf2SJohn Dyson 	 * XXX
20548a09cf2SJohn Dyson 	 * While this is restricted to root, we should probably figure out
20648a09cf2SJohn Dyson 	 * whether any other driver is using this i/o address, as so not to
20748a09cf2SJohn Dyson 	 * cause confusion.  This probably requires a global 'usage registry'.
20848a09cf2SJohn Dyson 	 */
20948a09cf2SJohn Dyson 
210b40ce416SJulian Elischer 	if (td->td_pcb->pcb_ext == 0)
211b40ce416SJulian Elischer 		if ((error = i386_extend_pcb(td)) != 0)
21248a09cf2SJohn Dyson 			return (error);
213b40ce416SJulian Elischer 	iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
21448a09cf2SJohn Dyson 
2159729f279SBruce Evans 	if (ua.start + ua.length > IOPAGES * PAGE_SIZE * NBBY)
21648a09cf2SJohn Dyson 		return (EINVAL);
21748a09cf2SJohn Dyson 
218ec9ed619SJonathan Lemon 	for (i = ua.start; i < ua.start + ua.length; i++) {
21948a09cf2SJohn Dyson 		if (ua.enable)
22048a09cf2SJohn Dyson 			iomap[i >> 3] &= ~(1 << (i & 7));
22148a09cf2SJohn Dyson 		else
22248a09cf2SJohn Dyson 			iomap[i >> 3] |= (1 << (i & 7));
22348a09cf2SJohn Dyson 	}
22448a09cf2SJohn Dyson 	return (error);
22548a09cf2SJohn Dyson }
22648a09cf2SJohn Dyson 
22748a09cf2SJohn Dyson static int
228b40ce416SJulian Elischer i386_get_ioperm(td, args)
229b40ce416SJulian Elischer 	struct thread *td;
23048a09cf2SJohn Dyson 	char *args;
23148a09cf2SJohn Dyson {
2329729f279SBruce Evans 	int i, state, error;
23348a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
23448a09cf2SJohn Dyson 	char *iomap;
23548a09cf2SJohn Dyson 
2360a5e03ddSMatthew Dillon 	if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
23748a09cf2SJohn Dyson 		return (error);
2389729f279SBruce Evans 	if (ua.start >= IOPAGES * PAGE_SIZE * NBBY)
2399729f279SBruce Evans 		return (EINVAL);
24048a09cf2SJohn Dyson 
241b40ce416SJulian Elischer 	if (td->td_pcb->pcb_ext == 0) {
24248a09cf2SJohn Dyson 		ua.length = 0;
24348a09cf2SJohn Dyson 		goto done;
24448a09cf2SJohn Dyson 	}
24548a09cf2SJohn Dyson 
246b40ce416SJulian Elischer 	iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
24748a09cf2SJohn Dyson 
24838861023SPeter Wemm 	i = ua.start;
24938861023SPeter Wemm 	state = (iomap[i >> 3] >> (i & 7)) & 1;
25048a09cf2SJohn Dyson 	ua.enable = !state;
25148a09cf2SJohn Dyson 	ua.length = 1;
25248a09cf2SJohn Dyson 
2539729f279SBruce Evans 	for (i = ua.start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) {
25448a09cf2SJohn Dyson 		if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
25548a09cf2SJohn Dyson 			break;
25648a09cf2SJohn Dyson 		ua.length++;
25748a09cf2SJohn Dyson 	}
25848a09cf2SJohn Dyson 
25948a09cf2SJohn Dyson done:
26048a09cf2SJohn Dyson 	error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
26148a09cf2SJohn Dyson 	return (error);
26248a09cf2SJohn Dyson }
26348a09cf2SJohn Dyson 
2640dbf6d73SJordan K. Hubbard /*
2650dbf6d73SJordan K. Hubbard  * Update the GDT entry pointing to the LDT to point to the LDT of the
266f1532aadSPeter Wemm  * current process.
2671acf256dSJohn Baldwin  *
2681acf256dSJohn Baldwin  * This must be called with sched_lock held.  Unfortunately, we can't use a
2691acf256dSJohn Baldwin  * mtx_assert() here because cpu_switch() calls this function after changing
2701acf256dSJohn Baldwin  * curproc but before sched_lock's owner is updated in mi_switch().
2710dbf6d73SJordan K. Hubbard  */
272da59a31cSDavid Greenman void
27324db0459SJohn Baldwin set_user_ldt(struct mdproc *mdp)
274da59a31cSDavid Greenman {
27524db0459SJohn Baldwin 	struct proc_ldt *pldt;
27691c28bfdSLuoqi Chen 
27724db0459SJohn Baldwin 	pldt = mdp->md_ldt;
2785206bca1SLuoqi Chen #ifdef SMP
27924db0459SJohn Baldwin 	gdt[PCPU_GET(cpuid) * NGDT + GUSERLDT_SEL].sd = pldt->ldt_sd;
2805206bca1SLuoqi Chen #else
28124db0459SJohn Baldwin 	gdt[GUSERLDT_SEL].sd = pldt->ldt_sd;
2825206bca1SLuoqi Chen #endif
283da59a31cSDavid Greenman 	lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
2844ef34f39SJake Burkholder 	PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL));
2851acf256dSJohn Baldwin }
2861acf256dSJohn Baldwin 
2875eb6e279SJohn Baldwin #ifdef SMP
2885eb6e279SJohn Baldwin static void
28924db0459SJohn Baldwin set_user_ldt_rv(struct thread *td)
2901acf256dSJohn Baldwin {
2911acf256dSJohn Baldwin 
292d8c586e7SDavid Xu 	if (td->td_proc != curthread->td_proc)
2931acf256dSJohn Baldwin 		return;
2941acf256dSJohn Baldwin 
29524db0459SJohn Baldwin 	set_user_ldt(&td->td_proc->p_md);
296da59a31cSDavid Greenman }
2975eb6e279SJohn Baldwin #endif
298da59a31cSDavid Greenman 
299df4d012bSJohn Baldwin /*
300df4d012bSJohn Baldwin  * Must be called with either sched_lock free or held but not recursed.
301df4d012bSJohn Baldwin  * If it does not return NULL, it will return with it owned.
302df4d012bSJohn Baldwin  */
30324db0459SJohn Baldwin struct proc_ldt *
30424db0459SJohn Baldwin user_ldt_alloc(struct mdproc *mdp, int len)
30591c28bfdSLuoqi Chen {
30624db0459SJohn Baldwin 	struct proc_ldt *pldt, *new_ldt;
30791c28bfdSLuoqi Chen 
308df4d012bSJohn Baldwin 	if (mtx_owned(&sched_lock))
309df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
310df4d012bSJohn Baldwin 	mtx_assert(&sched_lock, MA_NOTOWNED);
31124db0459SJohn Baldwin 	MALLOC(new_ldt, struct proc_ldt *, sizeof(struct proc_ldt),
312a163d034SWarner Losh 		M_SUBPROC, M_WAITOK);
31391c28bfdSLuoqi Chen 
31491c28bfdSLuoqi Chen 	new_ldt->ldt_len = len = NEW_MAX_LD(len);
31591c28bfdSLuoqi Chen 	new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
31691c28bfdSLuoqi Chen 		len * sizeof(union descriptor));
31791c28bfdSLuoqi Chen 	if (new_ldt->ldt_base == NULL) {
31891c28bfdSLuoqi Chen 		FREE(new_ldt, M_SUBPROC);
31991c28bfdSLuoqi Chen 		return NULL;
32091c28bfdSLuoqi Chen 	}
32191c28bfdSLuoqi Chen 	new_ldt->ldt_refcnt = 1;
32291c28bfdSLuoqi Chen 	new_ldt->ldt_active = 0;
32391c28bfdSLuoqi Chen 
324df4d012bSJohn Baldwin 	mtx_lock_spin(&sched_lock);
32591c28bfdSLuoqi Chen 	gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
32691c28bfdSLuoqi Chen 	gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
32791c28bfdSLuoqi Chen 	ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
32891c28bfdSLuoqi Chen 
32924db0459SJohn Baldwin 	if ((pldt = mdp->md_ldt)) {
33024db0459SJohn Baldwin 		if (len > pldt->ldt_len)
33124db0459SJohn Baldwin 			len = pldt->ldt_len;
33224db0459SJohn Baldwin 		bcopy(pldt->ldt_base, new_ldt->ldt_base,
33391c28bfdSLuoqi Chen 		    len * sizeof(union descriptor));
33491c28bfdSLuoqi Chen 	} else {
33591c28bfdSLuoqi Chen 		bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
33691c28bfdSLuoqi Chen 	}
33791c28bfdSLuoqi Chen 	return new_ldt;
33891c28bfdSLuoqi Chen }
33991c28bfdSLuoqi Chen 
340df4d012bSJohn Baldwin /*
341df4d012bSJohn Baldwin  * Must be called either with sched_lock free or held but not recursed.
34224db0459SJohn Baldwin  * If md_ldt is not NULL, it will return with sched_lock released.
343df4d012bSJohn Baldwin  */
34491c28bfdSLuoqi Chen void
34524db0459SJohn Baldwin user_ldt_free(struct thread *td)
34691c28bfdSLuoqi Chen {
34724db0459SJohn Baldwin 	struct mdproc *mdp = &td->td_proc->p_md;
34824db0459SJohn Baldwin 	struct proc_ldt *pldt = mdp->md_ldt;
34991c28bfdSLuoqi Chen 
35024db0459SJohn Baldwin 	if (pldt == NULL)
35191c28bfdSLuoqi Chen 		return;
35291c28bfdSLuoqi Chen 
353df4d012bSJohn Baldwin 	if (!mtx_owned(&sched_lock))
354df4d012bSJohn Baldwin 		mtx_lock_spin(&sched_lock);
355df4d012bSJohn Baldwin 	mtx_assert(&sched_lock, MA_OWNED | MA_NOTRECURSED);
35624db0459SJohn Baldwin 	if (td == PCPU_GET(curthread)) {
35791c28bfdSLuoqi Chen 		lldt(_default_ldt);
3584ef34f39SJake Burkholder 		PCPU_SET(currentldt, _default_ldt);
35991c28bfdSLuoqi Chen 	}
36091c28bfdSLuoqi Chen 
36124db0459SJohn Baldwin 	mdp->md_ldt = NULL;
36224db0459SJohn Baldwin 	if (--pldt->ldt_refcnt == 0) {
363df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
36424db0459SJohn Baldwin 		kmem_free(kernel_map, (vm_offset_t)pldt->ldt_base,
36524db0459SJohn Baldwin 			pldt->ldt_len * sizeof(union descriptor));
36624db0459SJohn Baldwin 		FREE(pldt, M_SUBPROC);
367df4d012bSJohn Baldwin 	} else
368df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
36991c28bfdSLuoqi Chen }
37091c28bfdSLuoqi Chen 
37187b91157SPoul-Henning Kamp static int
372b40ce416SJulian Elischer i386_get_ldt(td, args)
373b40ce416SJulian Elischer 	struct thread *td;
374da59a31cSDavid Greenman 	char *args;
375da59a31cSDavid Greenman {
376da59a31cSDavid Greenman 	int error = 0;
37724db0459SJohn Baldwin 	struct proc_ldt *pldt = td->td_proc->p_md.md_ldt;
378da59a31cSDavid Greenman 	int nldt, num;
379da59a31cSDavid Greenman 	union descriptor *lp;
3807a2bb3b8SLuoqi Chen 	struct i386_ldt_args ua, *uap = &ua;
381da59a31cSDavid Greenman 
3827a2bb3b8SLuoqi Chen 	if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
383da59a31cSDavid Greenman 		return(error);
384da59a31cSDavid Greenman 
385da59a31cSDavid Greenman #ifdef	DEBUG
38600671271SBruce Evans 	printf("i386_get_ldt: start=%d num=%d descs=%p\n",
3877a2bb3b8SLuoqi Chen 	    uap->start, uap->num, (void *)uap->descs);
388da59a31cSDavid Greenman #endif
389da59a31cSDavid Greenman 
3900dbf6d73SJordan K. Hubbard 	/* verify range of LDTs exist */
3910dbf6d73SJordan K. Hubbard 	if ((uap->start < 0) || (uap->num <= 0))
392da59a31cSDavid Greenman 		return(EINVAL);
393da59a31cSDavid Greenman 
39424db0459SJohn Baldwin 	if (pldt) {
39524db0459SJohn Baldwin 		nldt = pldt->ldt_len;
396da59a31cSDavid Greenman 		num = min(uap->num, nldt);
39724db0459SJohn Baldwin 		lp = &((union descriptor *)(pldt->ldt_base))[uap->start];
398da59a31cSDavid Greenman 	} else {
399da59a31cSDavid Greenman 		nldt = sizeof(ldt)/sizeof(ldt[0]);
400da59a31cSDavid Greenman 		num = min(uap->num, nldt);
401da59a31cSDavid Greenman 		lp = &ldt[uap->start];
402da59a31cSDavid Greenman 	}
4038b223ac2SPeter Wemm 	if (uap->start + num > nldt)
404da59a31cSDavid Greenman 		return(EINVAL);
405da59a31cSDavid Greenman 
4067a2bb3b8SLuoqi Chen 	error = copyout(lp, uap->descs, num * sizeof(union descriptor));
407da59a31cSDavid Greenman 	if (!error)
408b40ce416SJulian Elischer 		td->td_retval[0] = num;
409da59a31cSDavid Greenman 
410da59a31cSDavid Greenman 	return(error);
411da59a31cSDavid Greenman }
412da59a31cSDavid Greenman 
413f09fc81cSJulian Elischer #ifdef	DEBUG
414f09fc81cSJulian Elischer static int ldt_warnings;
415f09fc81cSJulian Elischer #define NUM_LDT_WARNINGS 10
416f09fc81cSJulian Elischer #endif
417f09fc81cSJulian Elischer 
41887b91157SPoul-Henning Kamp static int
419b40ce416SJulian Elischer i386_set_ldt(td, args)
420b40ce416SJulian Elischer 	struct thread *td;
421da59a31cSDavid Greenman 	char *args;
422da59a31cSDavid Greenman {
423dae8d52dSDavid Xu 	int error = 0, i;
4240dbf6d73SJordan K. Hubbard 	int largest_ld;
42524db0459SJohn Baldwin 	struct mdproc *mdp = &td->td_proc->p_md;
426dae8d52dSDavid Xu 	struct proc_ldt *pldt = 0;
4277a2bb3b8SLuoqi Chen 	struct i386_ldt_args ua, *uap = &ua;
428dae8d52dSDavid Xu 	union descriptor *descs, *dp;
429dae8d52dSDavid Xu 	int descs_size;
430dae8d52dSDavid Xu 
4317a2bb3b8SLuoqi Chen 	if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
432da59a31cSDavid Greenman 		return(error);
433da59a31cSDavid Greenman 
4341716a1afSJulian Elischer #ifdef	DEBUG
4351716a1afSJulian Elischer 	printf("i386_set_ldt: start=%d num=%d descs=%p\n",
4361716a1afSJulian Elischer 	    uap->start, uap->num, (void *)uap->descs);
4371716a1afSJulian Elischer #endif
4381716a1afSJulian Elischer 
439dae8d52dSDavid Xu 	if (uap->descs == NULL) {
440dae8d52dSDavid Xu 		/* Free descriptors */
441dae8d52dSDavid Xu 		if (uap->start == 0 && uap->num == 0) {
442dae8d52dSDavid Xu 			/*
443dae8d52dSDavid Xu 			 * Treat this as a special case, so userland needn't
444dae8d52dSDavid Xu 			 * know magic number NLDT.
445dae8d52dSDavid Xu 		 	 */
446dae8d52dSDavid Xu 			uap->start = NLDT;
447dae8d52dSDavid Xu 			uap->num = MAX_LD - NLDT;
4480dbf6d73SJordan K. Hubbard 		}
449f09fc81cSJulian Elischer 		if (uap->start <= LUDATA_SEL || uap->num <= 0)
450da59a31cSDavid Greenman 			return (EINVAL);
4511acf256dSJohn Baldwin 		mtx_lock_spin(&sched_lock);
452dae8d52dSDavid Xu 		pldt = mdp->md_ldt;
453dae8d52dSDavid Xu 		if (pldt == NULL || uap->start >= pldt->ldt_len) {
454e2e89358SDavid Xu 			mtx_unlock_spin(&sched_lock);
455dae8d52dSDavid Xu 			return (0);
456dae8d52dSDavid Xu 		}
457dae8d52dSDavid Xu 		largest_ld = uap->start + uap->num;
458dae8d52dSDavid Xu 		if (largest_ld > pldt->ldt_len)
459dae8d52dSDavid Xu 			largest_ld = pldt->ldt_len;
460dae8d52dSDavid Xu 		i = largest_ld - uap->start;
461dae8d52dSDavid Xu 		bzero(&((union descriptor *)(pldt->ldt_base))[uap->start],
462dae8d52dSDavid Xu 		    sizeof(union descriptor) * i);
463dae8d52dSDavid Xu 		mtx_unlock_spin(&sched_lock);
464dae8d52dSDavid Xu 		return (0);
465dae8d52dSDavid Xu 	}
466dae8d52dSDavid Xu 
467dae8d52dSDavid Xu 	if (!(uap->start == 0 && uap->num == 1)) {
468f09fc81cSJulian Elischer #ifdef	DEBUG
469f09fc81cSJulian Elischer 		/* complain a for a while if using old methods */
470f09fc81cSJulian Elischer 		if (ldt_warnings++ < NUM_LDT_WARNINGS) {
471f09fc81cSJulian Elischer 			printf("Warning: pid %d used static ldt allocation.\n",
472f09fc81cSJulian Elischer 			    td->td_proc->p_pid);
473f09fc81cSJulian Elischer 			printf("See the i386_set_ldt man page for more info\n");
474f09fc81cSJulian Elischer 		}
475f09fc81cSJulian Elischer #endif
476dae8d52dSDavid Xu 		/* verify range of descriptors to modify */
477dae8d52dSDavid Xu 		largest_ld = uap->start + uap->num;
478f09fc81cSJulian Elischer 		if (uap->start <= LUDATA_SEL || uap->start >= MAX_LD ||
479f09fc81cSJulian Elischer 		    uap->num < 0 || largest_ld > MAX_LD) {
480dae8d52dSDavid Xu 			return (EINVAL);
481dae8d52dSDavid Xu 		}
482da59a31cSDavid Greenman 	}
483da59a31cSDavid Greenman 
4848eb8107bSPeter Wemm 	descs_size = uap->num * sizeof(union descriptor);
4858eb8107bSPeter Wemm 	descs = (union descriptor *)kmem_alloc(kernel_map, descs_size);
4868eb8107bSPeter Wemm 	if (descs == NULL)
4878eb8107bSPeter Wemm 		return (ENOMEM);
488dae8d52dSDavid Xu 	error = copyin(uap->descs, descs, descs_size);
4898eb8107bSPeter Wemm 	if (error) {
4908eb8107bSPeter Wemm 		kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
4918eb8107bSPeter Wemm 		return (error);
4928eb8107bSPeter Wemm 	}
493dae8d52dSDavid Xu 
494da59a31cSDavid Greenman 	/* Check descriptors for access violations */
495dae8d52dSDavid Xu 	for (i = 0; i < uap->num; i++) {
4968eb8107bSPeter Wemm 		dp = &descs[i];
497da59a31cSDavid Greenman 
4988eb8107bSPeter Wemm 		switch (dp->sd.sd_type) {
4990dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL:	/* system null */
5008eb8107bSPeter Wemm 			dp->sd.sd_p = 0;
5010dbf6d73SJordan K. Hubbard 			break;
5020dbf6d73SJordan K. Hubbard 		case SDT_SYS286TSS: /* system 286 TSS available */
5030dbf6d73SJordan K. Hubbard 		case SDT_SYSLDT:    /* system local descriptor table */
5040dbf6d73SJordan K. Hubbard 		case SDT_SYS286BSY: /* system 286 TSS busy */
5050dbf6d73SJordan K. Hubbard 		case SDT_SYSTASKGT: /* system task gate */
5060dbf6d73SJordan K. Hubbard 		case SDT_SYS286IGT: /* system 286 interrupt gate */
5070dbf6d73SJordan K. Hubbard 		case SDT_SYS286TGT: /* system 286 trap gate */
5080dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL2:  /* undefined by Intel */
5090dbf6d73SJordan K. Hubbard 		case SDT_SYS386TSS: /* system 386 TSS available */
5100dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL3:  /* undefined by Intel */
5110dbf6d73SJordan K. Hubbard 		case SDT_SYS386BSY: /* system 386 TSS busy */
5120dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL4:  /* undefined by Intel */
5130dbf6d73SJordan K. Hubbard 		case SDT_SYS386IGT: /* system 386 interrupt gate */
5140dbf6d73SJordan K. Hubbard 		case SDT_SYS386TGT: /* system 386 trap gate */
5150dbf6d73SJordan K. Hubbard 		case SDT_SYS286CGT: /* system 286 call gate */
5160dbf6d73SJordan K. Hubbard 		case SDT_SYS386CGT: /* system 386 call gate */
5170dbf6d73SJordan K. Hubbard 			/* I can't think of any reason to allow a user proc
5180dbf6d73SJordan K. Hubbard 			 * to create a segment of these types.  They are
5190dbf6d73SJordan K. Hubbard 			 * for OS use only.
5200dbf6d73SJordan K. Hubbard 			 */
5218eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
522dae8d52dSDavid Xu 			return (EACCES);
523df4d012bSJohn Baldwin 			/*NOTREACHED*/
524da59a31cSDavid Greenman 
5250dbf6d73SJordan K. Hubbard 		/* memory segment types */
5260dbf6d73SJordan K. Hubbard 		case SDT_MEMEC:   /* memory execute only conforming */
5270dbf6d73SJordan K. Hubbard 		case SDT_MEMEAC:  /* memory execute only accessed conforming */
5280dbf6d73SJordan K. Hubbard 		case SDT_MEMERC:  /* memory execute read conforming */
5290dbf6d73SJordan K. Hubbard 		case SDT_MEMERAC: /* memory execute read accessed conforming */
5300dbf6d73SJordan K. Hubbard 			 /* Must be "present" if executable and conforming. */
5318eb8107bSPeter Wemm 			if (dp->sd.sd_p == 0) {
5328eb8107bSPeter Wemm 				kmem_free(kernel_map, (vm_offset_t)descs,
5338eb8107bSPeter Wemm 				    descs_size);
534da59a31cSDavid Greenman 				return (EACCES);
5358eb8107bSPeter Wemm 			}
536da59a31cSDavid Greenman 			break;
5370dbf6d73SJordan K. Hubbard 		case SDT_MEMRO:   /* memory read only */
5380dbf6d73SJordan K. Hubbard 		case SDT_MEMROA:  /* memory read only accessed */
5390dbf6d73SJordan K. Hubbard 		case SDT_MEMRW:   /* memory read write */
5400dbf6d73SJordan K. Hubbard 		case SDT_MEMRWA:  /* memory read write accessed */
5410dbf6d73SJordan K. Hubbard 		case SDT_MEMROD:  /* memory read only expand dwn limit */
5420dbf6d73SJordan K. Hubbard 		case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
5430dbf6d73SJordan K. Hubbard 		case SDT_MEMRWD:  /* memory read write expand dwn limit */
5440dbf6d73SJordan K. Hubbard 		case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
5450dbf6d73SJordan K. Hubbard 		case SDT_MEME:    /* memory execute only */
5460dbf6d73SJordan K. Hubbard 		case SDT_MEMEA:   /* memory execute only accessed */
5470dbf6d73SJordan K. Hubbard 		case SDT_MEMER:   /* memory execute read */
5480dbf6d73SJordan K. Hubbard 		case SDT_MEMERA:  /* memory execute read accessed */
549da59a31cSDavid Greenman 			break;
550da59a31cSDavid Greenman 		default:
5518eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5520dbf6d73SJordan K. Hubbard 			return(EINVAL);
553da59a31cSDavid Greenman 			/*NOTREACHED*/
554da59a31cSDavid Greenman 		}
5550dbf6d73SJordan K. Hubbard 
5560dbf6d73SJordan K. Hubbard 		/* Only user (ring-3) descriptors may be present. */
5578eb8107bSPeter Wemm 		if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) {
5588eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5590dbf6d73SJordan K. Hubbard 			return (EACCES);
560da59a31cSDavid Greenman 		}
5618eb8107bSPeter Wemm 	}
562da59a31cSDavid Greenman 
563dae8d52dSDavid Xu 	if (uap->start == 0 && uap->num == 1) {
564dae8d52dSDavid Xu 		/* Allocate a free slot */
565dae8d52dSDavid Xu 		pldt = mdp->md_ldt;
566dae8d52dSDavid Xu 		if (pldt == NULL) {
567dae8d52dSDavid Xu 			error = i386_ldt_grow(td, NLDT+1);
568dae8d52dSDavid Xu 			if (error) {
569dae8d52dSDavid Xu 				kmem_free(kernel_map, (vm_offset_t)descs,
570dae8d52dSDavid Xu 				    descs_size);
571dae8d52dSDavid Xu 				return (error);
572dae8d52dSDavid Xu 			}
573dae8d52dSDavid Xu 			pldt = mdp->md_ldt;
574dae8d52dSDavid Xu 		}
575dae8d52dSDavid Xu again:
576dae8d52dSDavid Xu 		mtx_lock_spin(&sched_lock);
577dae8d52dSDavid Xu 		dp = &((union descriptor *)(pldt->ldt_base))[NLDT];
578f09fc81cSJulian Elischer 		/*
579f09fc81cSJulian Elischer 		 * start scanning a bit up to leave room for NVidia and
580f09fc81cSJulian Elischer 		 * Wine, which still user the "Blat" method of allocation.
581f09fc81cSJulian Elischer 		 */
582e4e2c613SJulian Elischer 		for (i = NLDT; i < pldt->ldt_len; ++i) {
583dae8d52dSDavid Xu 			if (dp->sd.sd_type == SDT_SYSNULL)
584dae8d52dSDavid Xu 				break;
585dae8d52dSDavid Xu 			dp++;
586dae8d52dSDavid Xu 		}
587dae8d52dSDavid Xu 		if (i >= pldt->ldt_len) {
588dae8d52dSDavid Xu 			mtx_unlock_spin(&sched_lock);
589dae8d52dSDavid Xu 			error = i386_ldt_grow(td, pldt->ldt_len+1);
590dae8d52dSDavid Xu 			if (error) {
591dae8d52dSDavid Xu 				kmem_free(kernel_map, (vm_offset_t)descs,
592dae8d52dSDavid Xu 				    descs_size);
593dae8d52dSDavid Xu 				return (error);
594dae8d52dSDavid Xu 			}
595dae8d52dSDavid Xu 			goto again;
596dae8d52dSDavid Xu 		}
597dae8d52dSDavid Xu 		uap->start = i;
598dae8d52dSDavid Xu 		error = i386_set_ldt_data(td, i, 1, descs);
599dae8d52dSDavid Xu 		mtx_unlock_spin(&sched_lock);
600dae8d52dSDavid Xu 	} else {
601dae8d52dSDavid Xu 		largest_ld = uap->start + uap->num;
602dae8d52dSDavid Xu 		error = i386_ldt_grow(td, largest_ld);
603dae8d52dSDavid Xu 		if (error == 0) {
604dae8d52dSDavid Xu 			mtx_lock_spin(&sched_lock);
605dae8d52dSDavid Xu 			error = i386_set_ldt_data(td, uap->start, uap->num,
606dae8d52dSDavid Xu 			    descs);
607dae8d52dSDavid Xu 			mtx_unlock_spin(&sched_lock);
608dae8d52dSDavid Xu 		}
609dae8d52dSDavid Xu 	}
6108eb8107bSPeter Wemm 	kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
611dae8d52dSDavid Xu 	if (error == 0)
612dae8d52dSDavid Xu 		td->td_retval[0] = uap->start;
613dae8d52dSDavid Xu 	return (error);
614dae8d52dSDavid Xu }
615dae8d52dSDavid Xu 
616dae8d52dSDavid Xu static int
617dae8d52dSDavid Xu i386_set_ldt_data(struct thread *td, int start, int num,
618dae8d52dSDavid Xu 	union descriptor *descs)
619dae8d52dSDavid Xu {
620dae8d52dSDavid Xu 	struct mdproc *mdp = &td->td_proc->p_md;
621dae8d52dSDavid Xu 	struct proc_ldt *pldt = mdp->md_ldt;
622dae8d52dSDavid Xu 
623dae8d52dSDavid Xu 	mtx_assert(&sched_lock, MA_OWNED);
624dae8d52dSDavid Xu 
625dae8d52dSDavid Xu 	/* Fill in range */
626dae8d52dSDavid Xu 	bcopy(descs,
627dae8d52dSDavid Xu 	    &((union descriptor *)(pldt->ldt_base))[start],
628dae8d52dSDavid Xu 	    num * sizeof(union descriptor));
629dae8d52dSDavid Xu 	return (0);
630dae8d52dSDavid Xu }
631dae8d52dSDavid Xu 
632dae8d52dSDavid Xu static int
633dae8d52dSDavid Xu i386_ldt_grow(struct thread *td, int len)
634dae8d52dSDavid Xu {
635dae8d52dSDavid Xu 	struct mdproc *mdp = &td->td_proc->p_md;
636dae8d52dSDavid Xu 	struct proc_ldt *pldt;
637dae8d52dSDavid Xu 	caddr_t old_ldt_base;
638dae8d52dSDavid Xu 	int old_ldt_len;
639dae8d52dSDavid Xu 
640dae8d52dSDavid Xu 	if (len > MAX_LD)
641dae8d52dSDavid Xu 		return (ENOMEM);
642dae8d52dSDavid Xu 	if (len < NLDT+1)
643dae8d52dSDavid Xu 		len = NLDT+1;
644dae8d52dSDavid Xu 	pldt = mdp->md_ldt;
645dae8d52dSDavid Xu 	/* allocate user ldt */
646dae8d52dSDavid Xu 	if (!pldt || len > pldt->ldt_len) {
647dae8d52dSDavid Xu 		struct proc_ldt *new_ldt = user_ldt_alloc(mdp, len);
648dae8d52dSDavid Xu 		if (new_ldt == NULL)
649dae8d52dSDavid Xu 			return (ENOMEM);
650dae8d52dSDavid Xu 		pldt = mdp->md_ldt;
651dae8d52dSDavid Xu 		/* sched_lock was held by user_ldt_alloc */
652dae8d52dSDavid Xu 		if (pldt) {
653dae8d52dSDavid Xu 			if (new_ldt->ldt_len > pldt->ldt_len) {
654dae8d52dSDavid Xu 				old_ldt_base = pldt->ldt_base;
655dae8d52dSDavid Xu 				old_ldt_len = pldt->ldt_len;
656dae8d52dSDavid Xu 				pldt->ldt_sd = new_ldt->ldt_sd;
657dae8d52dSDavid Xu 				pldt->ldt_base = new_ldt->ldt_base;
658dae8d52dSDavid Xu 				pldt->ldt_len = new_ldt->ldt_len;
659dae8d52dSDavid Xu 				mtx_unlock_spin(&sched_lock);
660dae8d52dSDavid Xu 				kmem_free(kernel_map, (vm_offset_t)old_ldt_base,
661dae8d52dSDavid Xu 					old_ldt_len * sizeof(union descriptor));
662dae8d52dSDavid Xu 				FREE(new_ldt, M_SUBPROC);
663dae8d52dSDavid Xu 				mtx_lock_spin(&sched_lock);
664dae8d52dSDavid Xu 			} else {
665dae8d52dSDavid Xu 				/*
666dae8d52dSDavid Xu 				 * If other threads already did the work,
667dae8d52dSDavid Xu 				 * do nothing
668dae8d52dSDavid Xu 				 */
669dae8d52dSDavid Xu 				mtx_unlock_spin(&sched_lock);
670dae8d52dSDavid Xu 				kmem_free(kernel_map,
671dae8d52dSDavid Xu 				   (vm_offset_t)new_ldt->ldt_base,
672dae8d52dSDavid Xu 				   new_ldt->ldt_len * sizeof(union descriptor));
673dae8d52dSDavid Xu 				FREE(new_ldt, M_SUBPROC);
674dae8d52dSDavid Xu 				return (0);
675dae8d52dSDavid Xu 			}
676dae8d52dSDavid Xu 		} else {
677dae8d52dSDavid Xu 			mdp->md_ldt = pldt = new_ldt;
678dae8d52dSDavid Xu 		}
679dae8d52dSDavid Xu #ifdef SMP
680dae8d52dSDavid Xu 		mtx_unlock_spin(&sched_lock);
681dae8d52dSDavid Xu 		/* signal other cpus to reload ldt */
682dae8d52dSDavid Xu 		smp_rendezvous(NULL, (void (*)(void *))set_user_ldt_rv,
683dae8d52dSDavid Xu 		    NULL, td);
684dae8d52dSDavid Xu #else
685dae8d52dSDavid Xu 		set_user_ldt(mdp);
686dae8d52dSDavid Xu 		mtx_unlock_spin(&sched_lock);
687dae8d52dSDavid Xu #endif
688dae8d52dSDavid Xu 	}
6898eb8107bSPeter Wemm 	return (0);
690da59a31cSDavid Greenman }
691