xref: /freebsd/sys/i386/i386/sys_machdep.c (revision a163d034)
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
34c3aac50fSPeter Wemm  * $FreeBSD$
35812a11a2SDavid Greenman  *
365b81b6b3SRodney W. Grimes  */
375b81b6b3SRodney W. Grimes 
38b40ce416SJulian Elischer #include "opt_kstack_pages.h"
39268bdb43SPeter Wemm 
40f540b106SGarrett Wollman #include <sys/param.h>
41f540b106SGarrett Wollman #include <sys/systm.h>
42fb919e4dSMark Murray #include <sys/lock.h>
4391c28bfdSLuoqi Chen #include <sys/malloc.h>
44fb919e4dSMark Murray #include <sys/mutex.h>
45f540b106SGarrett Wollman #include <sys/proc.h>
466caa8a15SJohn Baldwin #include <sys/smp.h>
47fb919e4dSMark Murray #include <sys/sysproto.h>
48fb919e4dSMark Murray #include <sys/user.h>
49efeaf95aSDavid Greenman 
50efeaf95aSDavid Greenman #include <vm/vm.h>
51efeaf95aSDavid Greenman #include <vm/pmap.h>
52efeaf95aSDavid Greenman #include <vm/vm_map.h>
53efeaf95aSDavid Greenman #include <vm/vm_extern.h>
54efeaf95aSDavid Greenman 
55f540b106SGarrett Wollman #include <machine/cpu.h>
56b67dffdaSPeter Wemm #include <machine/pcb_ext.h>	/* pcb.h included by sys/user.h */
5724db0459SJohn Baldwin #include <machine/proc.h>
58f540b106SGarrett Wollman #include <machine/sysarch.h>
59812a11a2SDavid Greenman 
60f540b106SGarrett Wollman #include <vm/vm_kern.h>		/* for kernel_map */
61da59a31cSDavid Greenman 
620dbf6d73SJordan K. Hubbard #define MAX_LD 8192
630dbf6d73SJordan K. Hubbard #define LD_PER_PAGE 512
640dbf6d73SJordan K. Hubbard #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
650dbf6d73SJordan K. Hubbard #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
660dbf6d73SJordan K. Hubbard 
670dbf6d73SJordan K. Hubbard 
680dbf6d73SJordan K. Hubbard 
6915fe3067SAlfred Perlstein static int i386_get_ldt(struct thread *, char *);
7015fe3067SAlfred Perlstein static int i386_set_ldt(struct thread *, char *);
7115fe3067SAlfred Perlstein static int i386_get_ioperm(struct thread *, char *);
7215fe3067SAlfred Perlstein static int i386_set_ioperm(struct thread *, char *);
735eb6e279SJohn Baldwin #ifdef SMP
7415fe3067SAlfred Perlstein static void set_user_ldt_rv(struct thread *);
755eb6e279SJohn Baldwin #endif
765b81b6b3SRodney W. Grimes 
772f1ba63bSBruce Evans #ifndef _SYS_SYSPROTO_H_
78812a11a2SDavid Greenman struct sysarch_args {
79812a11a2SDavid Greenman 	int op;
80812a11a2SDavid Greenman 	char *parms;
8101ae5b20SDavid Greenman };
822f1ba63bSBruce Evans #endif
8301ae5b20SDavid Greenman 
84812a11a2SDavid Greenman int
85b40ce416SJulian Elischer sysarch(td, uap)
86b40ce416SJulian Elischer 	struct thread *td;
87812a11a2SDavid Greenman 	register struct sysarch_args *uap;
885b81b6b3SRodney W. Grimes {
89812a11a2SDavid Greenman 	int error = 0;
905b81b6b3SRodney W. Grimes 
91812a11a2SDavid Greenman 	switch(uap->op) {
92812a11a2SDavid Greenman 	case I386_GET_LDT:
93b40ce416SJulian Elischer 		error = i386_get_ldt(td, uap->parms);
945b81b6b3SRodney W. Grimes 		break;
955b81b6b3SRodney W. Grimes 
96812a11a2SDavid Greenman 	case I386_SET_LDT:
97b40ce416SJulian Elischer 		error = i386_set_ldt(td, uap->parms);
985b81b6b3SRodney W. Grimes 		break;
9948a09cf2SJohn Dyson 	case I386_GET_IOPERM:
100b40ce416SJulian Elischer 		error = i386_get_ioperm(td, uap->parms);
10148a09cf2SJohn Dyson 		break;
10248a09cf2SJohn Dyson 	case I386_SET_IOPERM:
103b40ce416SJulian Elischer 		error = i386_set_ioperm(td, uap->parms);
10448a09cf2SJohn Dyson 		break;
10548a09cf2SJohn Dyson 	case I386_VM86:
106b40ce416SJulian Elischer 		error = vm86_sysarch(td, uap->parms);
10748a09cf2SJohn Dyson 		break;
108812a11a2SDavid Greenman 	default:
1097a2bb3b8SLuoqi Chen 		error = EOPNOTSUPP;
110812a11a2SDavid Greenman 		break;
111812a11a2SDavid Greenman 	}
112812a11a2SDavid Greenman 	return (error);
113812a11a2SDavid Greenman }
114da59a31cSDavid Greenman 
11548a09cf2SJohn Dyson int
116b40ce416SJulian Elischer i386_extend_pcb(struct thread *td)
11748a09cf2SJohn Dyson {
11848a09cf2SJohn Dyson 	int i, offset;
11948a09cf2SJohn Dyson 	u_long *addr;
12048a09cf2SJohn Dyson 	struct pcb_ext *ext;
12148a09cf2SJohn Dyson 	struct soft_segment_descriptor ssd = {
12248a09cf2SJohn Dyson 		0,			/* segment base address (overwritten) */
12348a09cf2SJohn Dyson 		ctob(IOPAGES + 1) - 1,	/* length */
12448a09cf2SJohn Dyson 		SDT_SYS386TSS,		/* segment type */
12548a09cf2SJohn Dyson 		0,			/* priority level */
12648a09cf2SJohn Dyson 		1,			/* descriptor present */
12748a09cf2SJohn Dyson 		0, 0,
12848a09cf2SJohn Dyson 		0,			/* default 32 size */
12948a09cf2SJohn Dyson 		0			/* granularity */
13048a09cf2SJohn Dyson 	};
13148a09cf2SJohn Dyson 
132b40ce416SJulian Elischer 	if (td->td_proc->p_flag & P_KSES)
133b40ce416SJulian Elischer 		return (EINVAL);		/* XXXKSE */
134b40ce416SJulian Elischer /* XXXKSE  All the code below only works in 1:1   needs changing */
13548a09cf2SJohn Dyson 	ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
13648a09cf2SJohn Dyson 	if (ext == 0)
13748a09cf2SJohn Dyson 		return (ENOMEM);
138640c4313SJonathan Lemon 	bzero(ext, sizeof(struct pcb_ext));
139b40ce416SJulian Elischer 	/* -16 is so we can convert a trapframe into vm86trapframe inplace */
140b40ce416SJulian Elischer 	ext->ext_tss.tss_esp0 = td->td_kstack + ctob(KSTACK_PAGES) -
141b40ce416SJulian Elischer 	    sizeof(struct pcb) - 16;
14248a09cf2SJohn Dyson 	ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
14348a09cf2SJohn Dyson 	/*
14448a09cf2SJohn Dyson 	 * The last byte of the i/o map must be followed by an 0xff byte.
14548a09cf2SJohn Dyson 	 * We arbitrarily allocate 16 bytes here, to keep the starting
14648a09cf2SJohn Dyson 	 * address on a doubleword boundary.
14748a09cf2SJohn Dyson 	 */
14848a09cf2SJohn Dyson 	offset = PAGE_SIZE - 16;
14948a09cf2SJohn Dyson 	ext->ext_tss.tss_ioopt =
15048a09cf2SJohn Dyson 	    (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
15148a09cf2SJohn Dyson 	ext->ext_iomap = (caddr_t)ext + offset;
15248a09cf2SJohn Dyson 	ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
15348a09cf2SJohn Dyson 
15448a09cf2SJohn Dyson 	addr = (u_long *)ext->ext_vm86.vm86_intmap;
15548a09cf2SJohn Dyson 	for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
15648a09cf2SJohn Dyson 		*addr++ = ~0;
15748a09cf2SJohn Dyson 
15848a09cf2SJohn Dyson 	ssd.ssd_base = (unsigned)&ext->ext_tss;
15948a09cf2SJohn Dyson 	ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
16048a09cf2SJohn Dyson 	ssdtosd(&ssd, &ext->ext_tssd);
16148a09cf2SJohn Dyson 
1625e0f6bc4SPeter Wemm 	KASSERT(td->td_proc == curthread->td_proc, ("giving TSS to !curproc"));
163b40ce416SJulian Elischer 	KASSERT(td->td_pcb->pcb_ext == 0, ("already have a TSS!"));
1646caa8a15SJohn Baldwin 	mtx_lock_spin(&sched_lock);
165b40ce416SJulian Elischer 	td->td_pcb->pcb_ext = ext;
166df4d012bSJohn Baldwin 
167df4d012bSJohn Baldwin 	/* switch to the new TSS after syscall completes */
1684a338afdSJulian Elischer 	td->td_flags |= TDF_NEEDRESCHED;
1696caa8a15SJohn Baldwin 	mtx_unlock_spin(&sched_lock);
17048a09cf2SJohn Dyson 
17148a09cf2SJohn Dyson 	return 0;
17248a09cf2SJohn Dyson }
17348a09cf2SJohn Dyson 
17448a09cf2SJohn Dyson static int
175b40ce416SJulian Elischer i386_set_ioperm(td, args)
176b40ce416SJulian Elischer 	struct thread *td;
17748a09cf2SJohn Dyson 	char *args;
17848a09cf2SJohn Dyson {
1799729f279SBruce Evans 	int i, error;
18048a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
18148a09cf2SJohn Dyson 	char *iomap;
18248a09cf2SJohn Dyson 
1830a5e03ddSMatthew Dillon 	if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
18448a09cf2SJohn Dyson 		return (error);
18548a09cf2SJohn Dyson 
18644731cabSJohn Baldwin 	if ((error = suser(td)) != 0)
18748a09cf2SJohn Dyson 		return (error);
188a854ed98SJohn Baldwin 	if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
189330e7889SRobert Watson 		return (error);
19048a09cf2SJohn Dyson 	/*
19148a09cf2SJohn Dyson 	 * XXX
19248a09cf2SJohn Dyson 	 * While this is restricted to root, we should probably figure out
19348a09cf2SJohn Dyson 	 * whether any other driver is using this i/o address, as so not to
19448a09cf2SJohn Dyson 	 * cause confusion.  This probably requires a global 'usage registry'.
19548a09cf2SJohn Dyson 	 */
19648a09cf2SJohn Dyson 
197b40ce416SJulian Elischer 	if (td->td_pcb->pcb_ext == 0)
198b40ce416SJulian Elischer 		if ((error = i386_extend_pcb(td)) != 0)
19948a09cf2SJohn Dyson 			return (error);
200b40ce416SJulian Elischer 	iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
20148a09cf2SJohn Dyson 
2029729f279SBruce Evans 	if (ua.start + ua.length > IOPAGES * PAGE_SIZE * NBBY)
20348a09cf2SJohn Dyson 		return (EINVAL);
20448a09cf2SJohn Dyson 
205ec9ed619SJonathan Lemon 	for (i = ua.start; i < ua.start + ua.length; i++) {
20648a09cf2SJohn Dyson 		if (ua.enable)
20748a09cf2SJohn Dyson 			iomap[i >> 3] &= ~(1 << (i & 7));
20848a09cf2SJohn Dyson 		else
20948a09cf2SJohn Dyson 			iomap[i >> 3] |= (1 << (i & 7));
21048a09cf2SJohn Dyson 	}
21148a09cf2SJohn Dyson 	return (error);
21248a09cf2SJohn Dyson }
21348a09cf2SJohn Dyson 
21448a09cf2SJohn Dyson static int
215b40ce416SJulian Elischer i386_get_ioperm(td, args)
216b40ce416SJulian Elischer 	struct thread *td;
21748a09cf2SJohn Dyson 	char *args;
21848a09cf2SJohn Dyson {
2199729f279SBruce Evans 	int i, state, error;
22048a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
22148a09cf2SJohn Dyson 	char *iomap;
22248a09cf2SJohn Dyson 
2230a5e03ddSMatthew Dillon 	if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
22448a09cf2SJohn Dyson 		return (error);
2259729f279SBruce Evans 	if (ua.start >= IOPAGES * PAGE_SIZE * NBBY)
2269729f279SBruce Evans 		return (EINVAL);
22748a09cf2SJohn Dyson 
228b40ce416SJulian Elischer 	if (td->td_pcb->pcb_ext == 0) {
22948a09cf2SJohn Dyson 		ua.length = 0;
23048a09cf2SJohn Dyson 		goto done;
23148a09cf2SJohn Dyson 	}
23248a09cf2SJohn Dyson 
233b40ce416SJulian Elischer 	iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
23448a09cf2SJohn Dyson 
23538861023SPeter Wemm 	i = ua.start;
23638861023SPeter Wemm 	state = (iomap[i >> 3] >> (i & 7)) & 1;
23748a09cf2SJohn Dyson 	ua.enable = !state;
23848a09cf2SJohn Dyson 	ua.length = 1;
23948a09cf2SJohn Dyson 
2409729f279SBruce Evans 	for (i = ua.start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) {
24148a09cf2SJohn Dyson 		if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
24248a09cf2SJohn Dyson 			break;
24348a09cf2SJohn Dyson 		ua.length++;
24448a09cf2SJohn Dyson 	}
24548a09cf2SJohn Dyson 
24648a09cf2SJohn Dyson done:
24748a09cf2SJohn Dyson 	error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
24848a09cf2SJohn Dyson 	return (error);
24948a09cf2SJohn Dyson }
25048a09cf2SJohn Dyson 
2510dbf6d73SJordan K. Hubbard /*
2520dbf6d73SJordan K. Hubbard  * Update the GDT entry pointing to the LDT to point to the LDT of the
253f1532aadSPeter Wemm  * current process.
2541acf256dSJohn Baldwin  *
2551acf256dSJohn Baldwin  * This must be called with sched_lock held.  Unfortunately, we can't use a
2561acf256dSJohn Baldwin  * mtx_assert() here because cpu_switch() calls this function after changing
2571acf256dSJohn Baldwin  * curproc but before sched_lock's owner is updated in mi_switch().
2580dbf6d73SJordan K. Hubbard  */
259da59a31cSDavid Greenman void
26024db0459SJohn Baldwin set_user_ldt(struct mdproc *mdp)
261da59a31cSDavid Greenman {
26224db0459SJohn Baldwin 	struct proc_ldt *pldt;
26391c28bfdSLuoqi Chen 
26424db0459SJohn Baldwin 	pldt = mdp->md_ldt;
2655206bca1SLuoqi Chen #ifdef SMP
26624db0459SJohn Baldwin 	gdt[PCPU_GET(cpuid) * NGDT + GUSERLDT_SEL].sd = pldt->ldt_sd;
2675206bca1SLuoqi Chen #else
26824db0459SJohn Baldwin 	gdt[GUSERLDT_SEL].sd = pldt->ldt_sd;
2695206bca1SLuoqi Chen #endif
270da59a31cSDavid Greenman 	lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
2714ef34f39SJake Burkholder 	PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL));
2721acf256dSJohn Baldwin }
2731acf256dSJohn Baldwin 
2745eb6e279SJohn Baldwin #ifdef SMP
2755eb6e279SJohn Baldwin static void
27624db0459SJohn Baldwin set_user_ldt_rv(struct thread *td)
2771acf256dSJohn Baldwin {
2781acf256dSJohn Baldwin 
27924db0459SJohn Baldwin 	if (td != PCPU_GET(curthread))
2801acf256dSJohn Baldwin 		return;
2811acf256dSJohn Baldwin 
2821acf256dSJohn Baldwin 	mtx_lock_spin(&sched_lock);
28324db0459SJohn Baldwin 	set_user_ldt(&td->td_proc->p_md);
284df4d012bSJohn Baldwin 	mtx_unlock_spin(&sched_lock);
285da59a31cSDavid Greenman }
2865eb6e279SJohn Baldwin #endif
287da59a31cSDavid Greenman 
288df4d012bSJohn Baldwin /*
289df4d012bSJohn Baldwin  * Must be called with either sched_lock free or held but not recursed.
290df4d012bSJohn Baldwin  * If it does not return NULL, it will return with it owned.
291df4d012bSJohn Baldwin  */
29224db0459SJohn Baldwin struct proc_ldt *
29324db0459SJohn Baldwin user_ldt_alloc(struct mdproc *mdp, int len)
29491c28bfdSLuoqi Chen {
29524db0459SJohn Baldwin 	struct proc_ldt *pldt, *new_ldt;
29691c28bfdSLuoqi Chen 
297df4d012bSJohn Baldwin 	if (mtx_owned(&sched_lock))
298df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
299df4d012bSJohn Baldwin 	mtx_assert(&sched_lock, MA_NOTOWNED);
30024db0459SJohn Baldwin 	MALLOC(new_ldt, struct proc_ldt *, sizeof(struct proc_ldt),
301a163d034SWarner Losh 		M_SUBPROC, M_WAITOK);
30291c28bfdSLuoqi Chen 
30391c28bfdSLuoqi Chen 	new_ldt->ldt_len = len = NEW_MAX_LD(len);
30491c28bfdSLuoqi Chen 	new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
30591c28bfdSLuoqi Chen 		len * sizeof(union descriptor));
30691c28bfdSLuoqi Chen 	if (new_ldt->ldt_base == NULL) {
30791c28bfdSLuoqi Chen 		FREE(new_ldt, M_SUBPROC);
30891c28bfdSLuoqi Chen 		return NULL;
30991c28bfdSLuoqi Chen 	}
31091c28bfdSLuoqi Chen 	new_ldt->ldt_refcnt = 1;
31191c28bfdSLuoqi Chen 	new_ldt->ldt_active = 0;
31291c28bfdSLuoqi Chen 
313df4d012bSJohn Baldwin 	mtx_lock_spin(&sched_lock);
31491c28bfdSLuoqi Chen 	gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
31591c28bfdSLuoqi Chen 	gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
31691c28bfdSLuoqi Chen 	ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
31791c28bfdSLuoqi Chen 
31824db0459SJohn Baldwin 	if ((pldt = mdp->md_ldt)) {
31924db0459SJohn Baldwin 		if (len > pldt->ldt_len)
32024db0459SJohn Baldwin 			len = pldt->ldt_len;
32124db0459SJohn Baldwin 		bcopy(pldt->ldt_base, new_ldt->ldt_base,
32291c28bfdSLuoqi Chen 		    len * sizeof(union descriptor));
32391c28bfdSLuoqi Chen 	} else {
32491c28bfdSLuoqi Chen 		bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
32591c28bfdSLuoqi Chen 	}
32691c28bfdSLuoqi Chen 	return new_ldt;
32791c28bfdSLuoqi Chen }
32891c28bfdSLuoqi Chen 
329df4d012bSJohn Baldwin /*
330df4d012bSJohn Baldwin  * Must be called either with sched_lock free or held but not recursed.
33124db0459SJohn Baldwin  * If md_ldt is not NULL, it will return with sched_lock released.
332df4d012bSJohn Baldwin  */
33391c28bfdSLuoqi Chen void
33424db0459SJohn Baldwin user_ldt_free(struct thread *td)
33591c28bfdSLuoqi Chen {
33624db0459SJohn Baldwin 	struct mdproc *mdp = &td->td_proc->p_md;
33724db0459SJohn Baldwin 	struct proc_ldt *pldt = mdp->md_ldt;
33891c28bfdSLuoqi Chen 
33924db0459SJohn Baldwin 	if (pldt == NULL)
34091c28bfdSLuoqi Chen 		return;
34191c28bfdSLuoqi Chen 
342df4d012bSJohn Baldwin 	if (!mtx_owned(&sched_lock))
343df4d012bSJohn Baldwin 		mtx_lock_spin(&sched_lock);
344df4d012bSJohn Baldwin 	mtx_assert(&sched_lock, MA_OWNED | MA_NOTRECURSED);
34524db0459SJohn Baldwin 	if (td == PCPU_GET(curthread)) {
34691c28bfdSLuoqi Chen 		lldt(_default_ldt);
3474ef34f39SJake Burkholder 		PCPU_SET(currentldt, _default_ldt);
34891c28bfdSLuoqi Chen 	}
34991c28bfdSLuoqi Chen 
35024db0459SJohn Baldwin 	mdp->md_ldt = NULL;
35124db0459SJohn Baldwin 	if (--pldt->ldt_refcnt == 0) {
352df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
35324db0459SJohn Baldwin 		kmem_free(kernel_map, (vm_offset_t)pldt->ldt_base,
35424db0459SJohn Baldwin 			pldt->ldt_len * sizeof(union descriptor));
35524db0459SJohn Baldwin 		FREE(pldt, M_SUBPROC);
356df4d012bSJohn Baldwin 	} else
357df4d012bSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
35891c28bfdSLuoqi Chen }
35991c28bfdSLuoqi Chen 
36087b91157SPoul-Henning Kamp static int
361b40ce416SJulian Elischer i386_get_ldt(td, args)
362b40ce416SJulian Elischer 	struct thread *td;
363da59a31cSDavid Greenman 	char *args;
364da59a31cSDavid Greenman {
365da59a31cSDavid Greenman 	int error = 0;
36624db0459SJohn Baldwin 	struct proc_ldt *pldt = td->td_proc->p_md.md_ldt;
367da59a31cSDavid Greenman 	int nldt, num;
368da59a31cSDavid Greenman 	union descriptor *lp;
3697a2bb3b8SLuoqi Chen 	struct i386_ldt_args ua, *uap = &ua;
370da59a31cSDavid Greenman 
3717a2bb3b8SLuoqi Chen 	if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
372da59a31cSDavid Greenman 		return(error);
373da59a31cSDavid Greenman 
374da59a31cSDavid Greenman #ifdef	DEBUG
37500671271SBruce Evans 	printf("i386_get_ldt: start=%d num=%d descs=%p\n",
3767a2bb3b8SLuoqi Chen 	    uap->start, uap->num, (void *)uap->descs);
377da59a31cSDavid Greenman #endif
378da59a31cSDavid Greenman 
3790dbf6d73SJordan K. Hubbard 	/* verify range of LDTs exist */
3800dbf6d73SJordan K. Hubbard 	if ((uap->start < 0) || (uap->num <= 0))
381da59a31cSDavid Greenman 		return(EINVAL);
382da59a31cSDavid Greenman 
38324db0459SJohn Baldwin 	if (pldt) {
38424db0459SJohn Baldwin 		nldt = pldt->ldt_len;
385da59a31cSDavid Greenman 		num = min(uap->num, nldt);
38624db0459SJohn Baldwin 		lp = &((union descriptor *)(pldt->ldt_base))[uap->start];
387da59a31cSDavid Greenman 	} else {
388da59a31cSDavid Greenman 		nldt = sizeof(ldt)/sizeof(ldt[0]);
389da59a31cSDavid Greenman 		num = min(uap->num, nldt);
390da59a31cSDavid Greenman 		lp = &ldt[uap->start];
391da59a31cSDavid Greenman 	}
3928b223ac2SPeter Wemm 	if (uap->start + num > nldt)
393da59a31cSDavid Greenman 		return(EINVAL);
394da59a31cSDavid Greenman 
3957a2bb3b8SLuoqi Chen 	error = copyout(lp, uap->descs, num * sizeof(union descriptor));
396da59a31cSDavid Greenman 	if (!error)
397b40ce416SJulian Elischer 		td->td_retval[0] = num;
398da59a31cSDavid Greenman 
399da59a31cSDavid Greenman 	return(error);
400da59a31cSDavid Greenman }
401da59a31cSDavid Greenman 
40287b91157SPoul-Henning Kamp static int
403b40ce416SJulian Elischer i386_set_ldt(td, args)
404b40ce416SJulian Elischer 	struct thread *td;
405da59a31cSDavid Greenman 	char *args;
406da59a31cSDavid Greenman {
407da59a31cSDavid Greenman 	int error = 0, i, n;
4080dbf6d73SJordan K. Hubbard 	int largest_ld;
40924db0459SJohn Baldwin 	struct mdproc *mdp = &td->td_proc->p_md;
41024db0459SJohn Baldwin 	struct proc_ldt *pldt = mdp->md_ldt;
4117a2bb3b8SLuoqi Chen 	struct i386_ldt_args ua, *uap = &ua;
4128eb8107bSPeter Wemm 	union descriptor *descs;
413df4d012bSJohn Baldwin 	caddr_t old_ldt_base;
4148eb8107bSPeter Wemm 	int descs_size, old_ldt_len;
415ba74981eSWarner Losh 	register_t savecrit;
416da59a31cSDavid Greenman 
4177a2bb3b8SLuoqi Chen 	if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
418da59a31cSDavid Greenman 		return(error);
419da59a31cSDavid Greenman 
420da59a31cSDavid Greenman #ifdef	DEBUG
42100671271SBruce Evans 	printf("i386_set_ldt: start=%d num=%d descs=%p\n",
4227a2bb3b8SLuoqi Chen 	    uap->start, uap->num, (void *)uap->descs);
423da59a31cSDavid Greenman #endif
424da59a31cSDavid Greenman 
4250dbf6d73SJordan K. Hubbard 	/* verify range of descriptors to modify */
4268bb9c4c7SSteven Wallace 	if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
4270dbf6d73SJordan K. Hubbard 		(uap->num > MAX_LD))
4280dbf6d73SJordan K. Hubbard 	{
429da59a31cSDavid Greenman 		return(EINVAL);
4300dbf6d73SJordan K. Hubbard 	}
4310dbf6d73SJordan K. Hubbard 	largest_ld = uap->start + uap->num - 1;
4320dbf6d73SJordan K. Hubbard 	if (largest_ld >= MAX_LD)
433da59a31cSDavid Greenman 		return(EINVAL);
434da59a31cSDavid Greenman 
435da59a31cSDavid Greenman 	/* allocate user ldt */
43624db0459SJohn Baldwin 	if (!pldt || largest_ld >= pldt->ldt_len) {
43724db0459SJohn Baldwin 		struct proc_ldt *new_ldt = user_ldt_alloc(mdp, largest_ld);
43891c28bfdSLuoqi Chen 		if (new_ldt == NULL)
4390dbf6d73SJordan K. Hubbard 			return ENOMEM;
44024db0459SJohn Baldwin 		if (pldt) {
44124db0459SJohn Baldwin 			old_ldt_base = pldt->ldt_base;
44224db0459SJohn Baldwin 			old_ldt_len = pldt->ldt_len;
44324db0459SJohn Baldwin 			pldt->ldt_sd = new_ldt->ldt_sd;
44424db0459SJohn Baldwin 			pldt->ldt_base = new_ldt->ldt_base;
44524db0459SJohn Baldwin 			pldt->ldt_len = new_ldt->ldt_len;
446df4d012bSJohn Baldwin 			mtx_unlock_spin(&sched_lock);
447df4d012bSJohn Baldwin 			kmem_free(kernel_map, (vm_offset_t)old_ldt_base,
448df4d012bSJohn Baldwin 				old_ldt_len * sizeof(union descriptor));
44991c28bfdSLuoqi Chen 			FREE(new_ldt, M_SUBPROC);
4501acf256dSJohn Baldwin #ifndef SMP
4511acf256dSJohn Baldwin 			mtx_lock_spin(&sched_lock);
4521acf256dSJohn Baldwin #endif
453df4d012bSJohn Baldwin 		} else {
45424db0459SJohn Baldwin 			mdp->md_ldt = pldt = new_ldt;
4551acf256dSJohn Baldwin #ifdef SMP
456df4d012bSJohn Baldwin 			mtx_unlock_spin(&sched_lock);
4571acf256dSJohn Baldwin #endif
458df4d012bSJohn Baldwin 		}
45991c28bfdSLuoqi Chen #ifdef SMP
46091c28bfdSLuoqi Chen 		/* signal other cpus to reload ldt */
46124db0459SJohn Baldwin 		smp_rendezvous(NULL, (void (*)(void *))set_user_ldt_rv,
46224db0459SJohn Baldwin 		    NULL, td);
46391c28bfdSLuoqi Chen #else
46424db0459SJohn Baldwin 		set_user_ldt(mdp);
4651acf256dSJohn Baldwin 		mtx_unlock_spin(&sched_lock);
46691c28bfdSLuoqi Chen #endif
467da59a31cSDavid Greenman 	}
468da59a31cSDavid Greenman 
4698eb8107bSPeter Wemm 	descs_size = uap->num * sizeof(union descriptor);
4708eb8107bSPeter Wemm 	descs = (union descriptor *)kmem_alloc(kernel_map, descs_size);
4718eb8107bSPeter Wemm 	if (descs == NULL)
4728eb8107bSPeter Wemm 		return (ENOMEM);
4738eb8107bSPeter Wemm 	error = copyin(&uap->descs[0], descs, descs_size);
4748eb8107bSPeter Wemm 	if (error) {
4758eb8107bSPeter Wemm 		kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
4768eb8107bSPeter Wemm 		return (error);
4778eb8107bSPeter Wemm 	}
478da59a31cSDavid Greenman 	/* Check descriptors for access violations */
479da59a31cSDavid Greenman 	for (i = 0, n = uap->start; i < uap->num; i++, n++) {
4808eb8107bSPeter Wemm 		union descriptor *dp;
4818eb8107bSPeter Wemm 		dp = &descs[i];
482da59a31cSDavid Greenman 
4838eb8107bSPeter Wemm 		switch (dp->sd.sd_type) {
4840dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL:	/* system null */
4858eb8107bSPeter Wemm 			dp->sd.sd_p = 0;
4860dbf6d73SJordan K. Hubbard 			break;
4870dbf6d73SJordan K. Hubbard 		case SDT_SYS286TSS: /* system 286 TSS available */
4880dbf6d73SJordan K. Hubbard 		case SDT_SYSLDT:    /* system local descriptor table */
4890dbf6d73SJordan K. Hubbard 		case SDT_SYS286BSY: /* system 286 TSS busy */
4900dbf6d73SJordan K. Hubbard 		case SDT_SYSTASKGT: /* system task gate */
4910dbf6d73SJordan K. Hubbard 		case SDT_SYS286IGT: /* system 286 interrupt gate */
4920dbf6d73SJordan K. Hubbard 		case SDT_SYS286TGT: /* system 286 trap gate */
4930dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL2:  /* undefined by Intel */
4940dbf6d73SJordan K. Hubbard 		case SDT_SYS386TSS: /* system 386 TSS available */
4950dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL3:  /* undefined by Intel */
4960dbf6d73SJordan K. Hubbard 		case SDT_SYS386BSY: /* system 386 TSS busy */
4970dbf6d73SJordan K. Hubbard 		case SDT_SYSNULL4:  /* undefined by Intel */
4980dbf6d73SJordan K. Hubbard 		case SDT_SYS386IGT: /* system 386 interrupt gate */
4990dbf6d73SJordan K. Hubbard 		case SDT_SYS386TGT: /* system 386 trap gate */
5000dbf6d73SJordan K. Hubbard 		case SDT_SYS286CGT: /* system 286 call gate */
5010dbf6d73SJordan K. Hubbard 		case SDT_SYS386CGT: /* system 386 call gate */
5020dbf6d73SJordan K. Hubbard 			/* I can't think of any reason to allow a user proc
5030dbf6d73SJordan K. Hubbard 			 * to create a segment of these types.  They are
5040dbf6d73SJordan K. Hubbard 			 * for OS use only.
5050dbf6d73SJordan K. Hubbard 			 */
5068eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5070dbf6d73SJordan K. Hubbard 			return EACCES;
508df4d012bSJohn Baldwin 			/*NOTREACHED*/
509da59a31cSDavid Greenman 
5100dbf6d73SJordan K. Hubbard 		/* memory segment types */
5110dbf6d73SJordan K. Hubbard 		case SDT_MEMEC:   /* memory execute only conforming */
5120dbf6d73SJordan K. Hubbard 		case SDT_MEMEAC:  /* memory execute only accessed conforming */
5130dbf6d73SJordan K. Hubbard 		case SDT_MEMERC:  /* memory execute read conforming */
5140dbf6d73SJordan K. Hubbard 		case SDT_MEMERAC: /* memory execute read accessed conforming */
5150dbf6d73SJordan K. Hubbard 			 /* Must be "present" if executable and conforming. */
5168eb8107bSPeter Wemm 			if (dp->sd.sd_p == 0) {
5178eb8107bSPeter Wemm 				kmem_free(kernel_map, (vm_offset_t)descs,
5188eb8107bSPeter Wemm 				    descs_size);
519da59a31cSDavid Greenman 				return (EACCES);
5208eb8107bSPeter Wemm 			}
521da59a31cSDavid Greenman 			break;
5220dbf6d73SJordan K. Hubbard 		case SDT_MEMRO:   /* memory read only */
5230dbf6d73SJordan K. Hubbard 		case SDT_MEMROA:  /* memory read only accessed */
5240dbf6d73SJordan K. Hubbard 		case SDT_MEMRW:   /* memory read write */
5250dbf6d73SJordan K. Hubbard 		case SDT_MEMRWA:  /* memory read write accessed */
5260dbf6d73SJordan K. Hubbard 		case SDT_MEMROD:  /* memory read only expand dwn limit */
5270dbf6d73SJordan K. Hubbard 		case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
5280dbf6d73SJordan K. Hubbard 		case SDT_MEMRWD:  /* memory read write expand dwn limit */
5290dbf6d73SJordan K. Hubbard 		case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
5300dbf6d73SJordan K. Hubbard 		case SDT_MEME:    /* memory execute only */
5310dbf6d73SJordan K. Hubbard 		case SDT_MEMEA:   /* memory execute only accessed */
5320dbf6d73SJordan K. Hubbard 		case SDT_MEMER:   /* memory execute read */
5330dbf6d73SJordan K. Hubbard 		case SDT_MEMERA:  /* memory execute read accessed */
534da59a31cSDavid Greenman 			break;
535da59a31cSDavid Greenman 		default:
5368eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5370dbf6d73SJordan K. Hubbard 			return(EINVAL);
538da59a31cSDavid Greenman 			/*NOTREACHED*/
539da59a31cSDavid Greenman 		}
5400dbf6d73SJordan K. Hubbard 
5410dbf6d73SJordan K. Hubbard 		/* Only user (ring-3) descriptors may be present. */
5428eb8107bSPeter Wemm 		if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) {
5438eb8107bSPeter Wemm 			kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5440dbf6d73SJordan K. Hubbard 			return (EACCES);
545da59a31cSDavid Greenman 		}
5468eb8107bSPeter Wemm 	}
547da59a31cSDavid Greenman 
548da59a31cSDavid Greenman 	/* Fill in range */
549ba74981eSWarner Losh 	savecrit = intr_disable();
550fa778f80SJohn Baldwin 	bcopy(descs,
55124db0459SJohn Baldwin 	    &((union descriptor *)(pldt->ldt_base))[uap->start],
5520dbf6d73SJordan K. Hubbard 	    uap->num * sizeof(union descriptor));
553b40ce416SJulian Elischer 	td->td_retval[0] = uap->start;
554ba74981eSWarner Losh 	intr_restore(savecrit);
5558eb8107bSPeter Wemm 	kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
5568eb8107bSPeter Wemm 	return (0);
557da59a31cSDavid Greenman }
558