xref: /freebsd/sys/i386/i386/sys_machdep.c (revision 48a09cf2)
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
3448a09cf2SJohn Dyson  *	$Id: sys_machdep.c,v 1.22 1997/07/20 08:37:23 bde Exp $
35812a11a2SDavid Greenman  *
365b81b6b3SRodney W. Grimes  */
375b81b6b3SRodney W. Grimes 
38db6a20e2SGarrett Wollman #include "opt_user_ldt.h"
39f540b106SGarrett Wollman #include <sys/param.h>
40f540b106SGarrett Wollman #include <sys/systm.h>
412f1ba63bSBruce Evans #include <sys/sysproto.h>
42f540b106SGarrett Wollman #include <sys/proc.h>
43efeaf95aSDavid Greenman 
44efeaf95aSDavid Greenman #include <vm/vm.h>
45996c772fSJohn Dyson #include <sys/lock.h>
46efeaf95aSDavid Greenman #include <vm/pmap.h>
47efeaf95aSDavid Greenman #include <vm/vm_map.h>
48efeaf95aSDavid Greenman #include <vm/vm_extern.h>
49efeaf95aSDavid Greenman 
50f540b106SGarrett Wollman #include <sys/user.h>
51812a11a2SDavid Greenman 
52f540b106SGarrett Wollman #include <machine/cpu.h>
53f540b106SGarrett Wollman #include <machine/sysarch.h>
5448a09cf2SJohn Dyson #include <machine/pcb_ext.h>
55812a11a2SDavid Greenman 
56f540b106SGarrett Wollman #include <vm/vm_kern.h>		/* for kernel_map */
57da59a31cSDavid Greenman 
580dbf6d73SJordan K. Hubbard #define MAX_LD 8192
590dbf6d73SJordan K. Hubbard #define LD_PER_PAGE 512
600dbf6d73SJordan K. Hubbard #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
610dbf6d73SJordan K. Hubbard #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
620dbf6d73SJordan K. Hubbard 
630dbf6d73SJordan K. Hubbard 
640dbf6d73SJordan K. Hubbard 
65812a11a2SDavid Greenman void set_user_ldt	__P((struct pcb *pcb));
660b5b0f16SGarrett Wollman #ifdef USER_LDT
6787b91157SPoul-Henning Kamp static int i386_get_ldt	__P((struct proc *, char *, int *));
6887b91157SPoul-Henning Kamp static int i386_set_ldt	__P((struct proc *, char *, int *));
690b5b0f16SGarrett Wollman #endif
7048a09cf2SJohn Dyson #ifdef VM86
7148a09cf2SJohn Dyson static int i386_get_ioperm	__P((struct proc *, char *, int *));
7248a09cf2SJohn Dyson static int i386_set_ioperm	__P((struct proc *, char *, int *));
7348a09cf2SJohn Dyson int i386_extend_pcb	__P((struct proc *));
7448a09cf2SJohn Dyson int (*vm86_sysarch) __P((struct proc *, char *, int *));
7548a09cf2SJohn Dyson #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
85812a11a2SDavid Greenman sysarch(p, uap, retval)
8601ae5b20SDavid Greenman 	struct proc *p;
87812a11a2SDavid Greenman 	register struct sysarch_args *uap;
885b81b6b3SRodney W. Grimes 	int *retval;
895b81b6b3SRodney W. Grimes {
90812a11a2SDavid Greenman 	int error = 0;
915b81b6b3SRodney W. Grimes 
92812a11a2SDavid Greenman 	switch(uap->op) {
93812a11a2SDavid Greenman #ifdef	USER_LDT
94812a11a2SDavid Greenman 	case I386_GET_LDT:
95812a11a2SDavid Greenman 		error = i386_get_ldt(p, uap->parms, retval);
965b81b6b3SRodney W. Grimes 		break;
975b81b6b3SRodney W. Grimes 
98812a11a2SDavid Greenman 	case I386_SET_LDT:
99812a11a2SDavid Greenman 		error = i386_set_ldt(p, uap->parms, retval);
1005b81b6b3SRodney W. Grimes 		break;
1015b81b6b3SRodney W. Grimes #endif
10248a09cf2SJohn Dyson #ifdef VM86
10348a09cf2SJohn Dyson 	case I386_GET_IOPERM:
10448a09cf2SJohn Dyson 		error = i386_get_ioperm(p, uap->parms, retval);
10548a09cf2SJohn Dyson 		break;
10648a09cf2SJohn Dyson 	case I386_SET_IOPERM:
10748a09cf2SJohn Dyson 		error = i386_set_ioperm(p, uap->parms, retval);
10848a09cf2SJohn Dyson 		break;
10948a09cf2SJohn Dyson 	case I386_VM86:
11048a09cf2SJohn Dyson 		if (vm86_sysarch) {
11148a09cf2SJohn Dyson 			error = (*vm86_sysarch)(p, uap->parms, retval);
11248a09cf2SJohn Dyson 			break;
11348a09cf2SJohn Dyson 		}
11448a09cf2SJohn Dyson 		/* FALL THROUGH */
11548a09cf2SJohn Dyson #endif
116812a11a2SDavid Greenman 	default:
117812a11a2SDavid Greenman 		error = EINVAL;
118812a11a2SDavid Greenman 		break;
119812a11a2SDavid Greenman 	}
120812a11a2SDavid Greenman 	return (error);
121812a11a2SDavid Greenman }
122da59a31cSDavid Greenman 
12348a09cf2SJohn Dyson #ifdef VM86
12448a09cf2SJohn Dyson int
12548a09cf2SJohn Dyson i386_extend_pcb(struct proc *p)
12648a09cf2SJohn Dyson {
12748a09cf2SJohn Dyson 	int i, offset;
12848a09cf2SJohn Dyson 	u_long *addr;
12948a09cf2SJohn Dyson 	struct pcb_ext *ext;
13048a09cf2SJohn Dyson 	struct segment_descriptor sd;
13148a09cf2SJohn Dyson 	struct soft_segment_descriptor ssd = {
13248a09cf2SJohn Dyson 		0,			/* segment base address (overwritten) */
13348a09cf2SJohn Dyson 		ctob(IOPAGES + 1) - 1,	/* length */
13448a09cf2SJohn Dyson 		SDT_SYS386TSS,		/* segment type */
13548a09cf2SJohn Dyson 		0,			/* priority level */
13648a09cf2SJohn Dyson 		1,			/* descriptor present */
13748a09cf2SJohn Dyson 		0, 0,
13848a09cf2SJohn Dyson 		0,			/* default 32 size */
13948a09cf2SJohn Dyson 		0			/* granularity */
14048a09cf2SJohn Dyson 	};
14148a09cf2SJohn Dyson 
14248a09cf2SJohn Dyson 	ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
14348a09cf2SJohn Dyson 	if (ext == 0)
14448a09cf2SJohn Dyson 		return (ENOMEM);
14548a09cf2SJohn Dyson 	p->p_addr->u_pcb.pcb_ext = ext;
14648a09cf2SJohn Dyson 	bzero(&ext->ext_tss, sizeof(struct i386tss));
14748a09cf2SJohn Dyson 	ext->ext_tss.tss_esp0 = (unsigned)p->p_addr + ctob(UPAGES) - 16;
14848a09cf2SJohn Dyson         ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
14948a09cf2SJohn Dyson 	/*
15048a09cf2SJohn Dyson 	 * The last byte of the i/o map must be followed by an 0xff byte.
15148a09cf2SJohn Dyson 	 * We arbitrarily allocate 16 bytes here, to keep the starting
15248a09cf2SJohn Dyson 	 * address on a doubleword boundary.
15348a09cf2SJohn Dyson 	 */
15448a09cf2SJohn Dyson 	offset = PAGE_SIZE - 16;
15548a09cf2SJohn Dyson 	ext->ext_tss.tss_ioopt =
15648a09cf2SJohn Dyson 	    (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
15748a09cf2SJohn Dyson 	ext->ext_iomap = (caddr_t)ext + offset;
15848a09cf2SJohn Dyson 	ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
15948a09cf2SJohn Dyson 	ext->ext_vm86.vm86_inited = 0;
16048a09cf2SJohn Dyson 
16148a09cf2SJohn Dyson 	addr = (u_long *)ext->ext_vm86.vm86_intmap;
16248a09cf2SJohn Dyson 	for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
16348a09cf2SJohn Dyson 		*addr++ = ~0;
16448a09cf2SJohn Dyson 
16548a09cf2SJohn Dyson 	ssd.ssd_base = (unsigned)&ext->ext_tss;
16648a09cf2SJohn Dyson 	ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
16748a09cf2SJohn Dyson 	ssdtosd(&ssd, &ext->ext_tssd);
16848a09cf2SJohn Dyson 
16948a09cf2SJohn Dyson 	/* switch to the new TSS after syscall completes */
17048a09cf2SJohn Dyson 	need_resched();
17148a09cf2SJohn Dyson 
17248a09cf2SJohn Dyson 	return 0;
17348a09cf2SJohn Dyson }
17448a09cf2SJohn Dyson 
17548a09cf2SJohn Dyson struct i386_ioperm_args {
17648a09cf2SJohn Dyson 	u_short start;
17748a09cf2SJohn Dyson 	u_short length;
17848a09cf2SJohn Dyson 	u_char enable;
17948a09cf2SJohn Dyson };
18048a09cf2SJohn Dyson 
18148a09cf2SJohn Dyson static int
18248a09cf2SJohn Dyson i386_set_ioperm(p, args, retval)
18348a09cf2SJohn Dyson 	struct proc *p;
18448a09cf2SJohn Dyson 	char *args;
18548a09cf2SJohn Dyson 	int *retval;
18648a09cf2SJohn Dyson {
18748a09cf2SJohn Dyson 	int i, error = 0;
18848a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
18948a09cf2SJohn Dyson 	char *iomap;
19048a09cf2SJohn Dyson 
19148a09cf2SJohn Dyson 	if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args)))
19248a09cf2SJohn Dyson 		return (error);
19348a09cf2SJohn Dyson 
19448a09cf2SJohn Dyson         /* Only root can do this */
19548a09cf2SJohn Dyson         if (error = suser(p->p_ucred, &p->p_acflag))
19648a09cf2SJohn Dyson                 return (error);
19748a09cf2SJohn Dyson 	/*
19848a09cf2SJohn Dyson 	 * XXX
19948a09cf2SJohn Dyson 	 * While this is restricted to root, we should probably figure out
20048a09cf2SJohn Dyson 	 * whether any other driver is using this i/o address, as so not to
20148a09cf2SJohn Dyson 	 * cause confusion.  This probably requires a global 'usage registry'.
20248a09cf2SJohn Dyson 	 */
20348a09cf2SJohn Dyson 
20448a09cf2SJohn Dyson 	if (p->p_addr->u_pcb.pcb_ext == 0)
20548a09cf2SJohn Dyson 		if (error = i386_extend_pcb(p))
20648a09cf2SJohn Dyson 			return (error);
20748a09cf2SJohn Dyson 	iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
20848a09cf2SJohn Dyson 
20948a09cf2SJohn Dyson 	if ((int)(ua.start + ua.length) > 0xffff)
21048a09cf2SJohn Dyson 		return (EINVAL);
21148a09cf2SJohn Dyson 
21248a09cf2SJohn Dyson 	for (i = ua.start; i < (int)(ua.start + ua.length) + 1; i++) {
21348a09cf2SJohn Dyson 		if (ua.enable)
21448a09cf2SJohn Dyson 			iomap[i >> 3] &= ~(1 << (i & 7));
21548a09cf2SJohn Dyson 		else
21648a09cf2SJohn Dyson 			iomap[i >> 3] |= (1 << (i & 7));
21748a09cf2SJohn Dyson 	}
21848a09cf2SJohn Dyson 	return (error);
21948a09cf2SJohn Dyson }
22048a09cf2SJohn Dyson 
22148a09cf2SJohn Dyson static int
22248a09cf2SJohn Dyson i386_get_ioperm(p, args, retval)
22348a09cf2SJohn Dyson 	struct proc *p;
22448a09cf2SJohn Dyson 	char *args;
22548a09cf2SJohn Dyson 	int *retval;
22648a09cf2SJohn Dyson {
22748a09cf2SJohn Dyson 	int i, state, error = 0;
22848a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
22948a09cf2SJohn Dyson 	char *iomap;
23048a09cf2SJohn Dyson 
23148a09cf2SJohn Dyson 	if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args)))
23248a09cf2SJohn Dyson 		return (error);
23348a09cf2SJohn Dyson 
23448a09cf2SJohn Dyson 	if (p->p_addr->u_pcb.pcb_ext == 0) {
23548a09cf2SJohn Dyson 		ua.length = 0;
23648a09cf2SJohn Dyson 		goto done;
23748a09cf2SJohn Dyson 	}
23848a09cf2SJohn Dyson 
23948a09cf2SJohn Dyson 	iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
24048a09cf2SJohn Dyson 
24148a09cf2SJohn Dyson 	state = (iomap[i >> 3] >> (i & 7)) & 1;
24248a09cf2SJohn Dyson 	ua.enable = !state;
24348a09cf2SJohn Dyson 	ua.length = 1;
24448a09cf2SJohn Dyson 
24548a09cf2SJohn Dyson 	for (i = ua.start + 1; i < 0x10000; i++) {
24648a09cf2SJohn Dyson 		if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
24748a09cf2SJohn Dyson 			break;
24848a09cf2SJohn Dyson 		ua.length++;
24948a09cf2SJohn Dyson 	}
25048a09cf2SJohn Dyson 
25148a09cf2SJohn Dyson done:
25248a09cf2SJohn Dyson 	error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
25348a09cf2SJohn Dyson 	return (error);
25448a09cf2SJohn Dyson }
25548a09cf2SJohn Dyson #endif /* VM86 */
25648a09cf2SJohn Dyson 
257da59a31cSDavid Greenman #ifdef USER_LDT
2580dbf6d73SJordan K. Hubbard /*
2590dbf6d73SJordan K. Hubbard  * Update the GDT entry pointing to the LDT to point to the LDT of the
2600dbf6d73SJordan K. Hubbard  * current process.
2610dbf6d73SJordan K. Hubbard  */
262da59a31cSDavid Greenman void
263da59a31cSDavid Greenman set_user_ldt(struct pcb *pcb)
264da59a31cSDavid Greenman {
265da59a31cSDavid Greenman 	gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt;
266da59a31cSDavid Greenman 	gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1;
2676d520d66SBruce Evans 	ssdtosd(&gdt_segs[GUSERLDT_SEL], &gdt[GUSERLDT_SEL].sd);
268da59a31cSDavid Greenman 	lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
269da59a31cSDavid Greenman 	currentldt = GSEL(GUSERLDT_SEL, SEL_KPL);
270da59a31cSDavid Greenman }
271da59a31cSDavid Greenman 
272da59a31cSDavid Greenman struct i386_get_ldt_args {
273da59a31cSDavid Greenman 	int start;
274da59a31cSDavid Greenman 	union descriptor *desc;
275da59a31cSDavid Greenman 	int num;
276da59a31cSDavid Greenman };
277da59a31cSDavid Greenman 
27887b91157SPoul-Henning Kamp static int
279da59a31cSDavid Greenman i386_get_ldt(p, args, retval)
280da59a31cSDavid Greenman 	struct proc *p;
281da59a31cSDavid Greenman 	char *args;
282da59a31cSDavid Greenman 	int *retval;
283da59a31cSDavid Greenman {
284da59a31cSDavid Greenman 	int error = 0;
285da59a31cSDavid Greenman 	struct pcb *pcb = &p->p_addr->u_pcb;
286da59a31cSDavid Greenman 	int nldt, num;
287da59a31cSDavid Greenman 	union descriptor *lp;
288da59a31cSDavid Greenman 	int s;
2890dbf6d73SJordan K. Hubbard 	struct i386_get_ldt_args ua;
2900dbf6d73SJordan K. Hubbard 	struct i386_get_ldt_args *uap = &ua;
291da59a31cSDavid Greenman 
2920dbf6d73SJordan K. Hubbard 	if ((error = copyin(args, uap, sizeof(struct i386_get_ldt_args))) < 0)
293da59a31cSDavid Greenman 		return(error);
294da59a31cSDavid Greenman 
295da59a31cSDavid Greenman #ifdef	DEBUG
2960dbf6d73SJordan K. Hubbard 	printf("i386_get_ldt: start=%d num=%d descs=%x\n", uap->start,
2970dbf6d73SJordan K. Hubbard 		uap->num, uap->desc);
298da59a31cSDavid Greenman #endif
299da59a31cSDavid Greenman 
3000dbf6d73SJordan K. Hubbard 	/* verify range of LDTs exist */
3010dbf6d73SJordan K. Hubbard 	if ((uap->start < 0) || (uap->num <= 0))
302da59a31cSDavid Greenman 		return(EINVAL);
303da59a31cSDavid Greenman 
304da59a31cSDavid Greenman 	s = splhigh();
305da59a31cSDavid Greenman 
306da59a31cSDavid Greenman 	if (pcb->pcb_ldt) {
307da59a31cSDavid Greenman 		nldt = pcb->pcb_ldt_len;
308da59a31cSDavid Greenman 		num = min(uap->num, nldt);
309da59a31cSDavid Greenman 		lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start];
310da59a31cSDavid Greenman 	} else {
311da59a31cSDavid Greenman 		nldt = sizeof(ldt)/sizeof(ldt[0]);
312da59a31cSDavid Greenman 		num = min(uap->num, nldt);
313da59a31cSDavid Greenman 		lp = &ldt[uap->start];
314da59a31cSDavid Greenman 	}
315da59a31cSDavid Greenman 	if (uap->start > nldt) {
316da59a31cSDavid Greenman 		splx(s);
317da59a31cSDavid Greenman 		return(EINVAL);
318da59a31cSDavid Greenman 	}
319da59a31cSDavid Greenman 
320da59a31cSDavid Greenman 	error = copyout(lp, uap->desc, num * sizeof(union descriptor));
321da59a31cSDavid Greenman 	if (!error)
322da59a31cSDavid Greenman 		*retval = num;
323da59a31cSDavid Greenman 
324da59a31cSDavid Greenman 	splx(s);
325da59a31cSDavid Greenman 	return(error);
326da59a31cSDavid Greenman }
327da59a31cSDavid Greenman 
328da59a31cSDavid Greenman struct i386_set_ldt_args {
329da59a31cSDavid Greenman 	int start;
330da59a31cSDavid Greenman 	union descriptor *desc;
331da59a31cSDavid Greenman 	int num;
332da59a31cSDavid Greenman };
333da59a31cSDavid Greenman 
33487b91157SPoul-Henning Kamp static int
335da59a31cSDavid Greenman i386_set_ldt(p, args, retval)
336da59a31cSDavid Greenman 	struct proc *p;
337da59a31cSDavid Greenman 	char *args;
338da59a31cSDavid Greenman 	int *retval;
339da59a31cSDavid Greenman {
340da59a31cSDavid Greenman 	int error = 0, i, n;
3410dbf6d73SJordan K. Hubbard  	int largest_ld;
342da59a31cSDavid Greenman 	struct pcb *pcb = &p->p_addr->u_pcb;
343da59a31cSDavid Greenman 	int s;
344da59a31cSDavid Greenman 	struct i386_set_ldt_args ua, *uap;
345da59a31cSDavid Greenman 
346da59a31cSDavid Greenman 	if ((error = copyin(args, &ua, sizeof(struct i386_set_ldt_args))) < 0)
347da59a31cSDavid Greenman 		return(error);
348da59a31cSDavid Greenman 
349da59a31cSDavid Greenman 	uap = &ua;
350da59a31cSDavid Greenman 
351da59a31cSDavid Greenman #ifdef	DEBUG
352da59a31cSDavid Greenman 	printf("i386_set_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
353da59a31cSDavid Greenman #endif
354da59a31cSDavid Greenman 
3550dbf6d73SJordan K. Hubbard  	/* verify range of descriptors to modify */
3568bb9c4c7SSteven Wallace  	if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
3570dbf6d73SJordan K. Hubbard  		(uap->num > MAX_LD))
3580dbf6d73SJordan K. Hubbard  	{
359da59a31cSDavid Greenman  		return(EINVAL);
3600dbf6d73SJordan K. Hubbard  	}
3610dbf6d73SJordan K. Hubbard  	largest_ld = uap->start + uap->num - 1;
3620dbf6d73SJordan K. Hubbard  	if (largest_ld >= MAX_LD)
363da59a31cSDavid Greenman   		return(EINVAL);
364da59a31cSDavid Greenman 
365da59a31cSDavid Greenman   	/* allocate user ldt */
3660dbf6d73SJordan K. Hubbard  	if (!pcb->pcb_ldt || (largest_ld >= pcb->pcb_ldt_len)) {
3670dbf6d73SJordan K. Hubbard  		union descriptor *new_ldt = (union descriptor *)kmem_alloc(
3680dbf6d73SJordan K. Hubbard  			kernel_map, SIZE_FROM_LARGEST_LD(largest_ld));
3690dbf6d73SJordan K. Hubbard  		if (new_ldt == NULL) {
3700dbf6d73SJordan K. Hubbard  			return ENOMEM;
3710dbf6d73SJordan K. Hubbard  		}
3720dbf6d73SJordan K. Hubbard  		if (pcb->pcb_ldt) {
3730dbf6d73SJordan K. Hubbard  			bcopy(pcb->pcb_ldt, new_ldt, pcb->pcb_ldt_len
3740dbf6d73SJordan K. Hubbard  				* sizeof(union descriptor));
3750dbf6d73SJordan K. Hubbard  			kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
3760dbf6d73SJordan K. Hubbard  				pcb->pcb_ldt_len * sizeof(union descriptor));
3770dbf6d73SJordan K. Hubbard  		} else {
378da59a31cSDavid Greenman  			bcopy(ldt, new_ldt, sizeof(ldt));
3790dbf6d73SJordan K. Hubbard  		}
380da59a31cSDavid Greenman   		pcb->pcb_ldt = (caddr_t)new_ldt;
3810dbf6d73SJordan K. Hubbard  		pcb->pcb_ldt_len = NEW_MAX_LD(largest_ld);
3820dbf6d73SJordan K. Hubbard  		if (pcb == curpcb)
3830dbf6d73SJordan K. Hubbard  		    set_user_ldt(pcb);
384da59a31cSDavid Greenman   	}
385da59a31cSDavid Greenman 
386da59a31cSDavid Greenman 	/* Check descriptors for access violations */
387da59a31cSDavid Greenman 	for (i = 0, n = uap->start; i < uap->num; i++, n++) {
388da59a31cSDavid Greenman 		union descriptor desc, *dp;
389da59a31cSDavid Greenman 		dp = &uap->desc[i];
390da59a31cSDavid Greenman 		error = copyin(dp, &desc, sizeof(union descriptor));
391da59a31cSDavid Greenman 		if (error)
392da59a31cSDavid Greenman 			return(error);
393da59a31cSDavid Greenman 
3940dbf6d73SJordan K. Hubbard 		switch (desc.sd.sd_type) {
3950dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL:	/* system null */
3960dbf6d73SJordan K. Hubbard  			desc.sd.sd_p = 0;
3970dbf6d73SJordan K. Hubbard   			break;
3980dbf6d73SJordan K. Hubbard  		case SDT_SYS286TSS: /* system 286 TSS available */
3990dbf6d73SJordan K. Hubbard  		case SDT_SYSLDT:    /* system local descriptor table */
4000dbf6d73SJordan K. Hubbard  		case SDT_SYS286BSY: /* system 286 TSS busy */
4010dbf6d73SJordan K. Hubbard  		case SDT_SYSTASKGT: /* system task gate */
4020dbf6d73SJordan K. Hubbard  		case SDT_SYS286IGT: /* system 286 interrupt gate */
4030dbf6d73SJordan K. Hubbard  		case SDT_SYS286TGT: /* system 286 trap gate */
4040dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL2:  /* undefined by Intel */
4050dbf6d73SJordan K. Hubbard  		case SDT_SYS386TSS: /* system 386 TSS available */
4060dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL3:  /* undefined by Intel */
4070dbf6d73SJordan K. Hubbard  		case SDT_SYS386BSY: /* system 386 TSS busy */
4080dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL4:  /* undefined by Intel */
4090dbf6d73SJordan K. Hubbard  		case SDT_SYS386IGT: /* system 386 interrupt gate */
4100dbf6d73SJordan K. Hubbard  		case SDT_SYS386TGT: /* system 386 trap gate */
4110dbf6d73SJordan K. Hubbard  		case SDT_SYS286CGT: /* system 286 call gate */
4120dbf6d73SJordan K. Hubbard  		case SDT_SYS386CGT: /* system 386 call gate */
4130dbf6d73SJordan K. Hubbard  			/* I can't think of any reason to allow a user proc
4140dbf6d73SJordan K. Hubbard  			 * to create a segment of these types.  They are
4150dbf6d73SJordan K. Hubbard  			 * for OS use only.
4160dbf6d73SJordan K. Hubbard  			 */
4170dbf6d73SJordan K. Hubbard      	    	    	return EACCES;
418da59a31cSDavid Greenman 
4190dbf6d73SJordan K. Hubbard  		/* memory segment types */
4200dbf6d73SJordan K. Hubbard  		case SDT_MEMEC:   /* memory execute only conforming */
4210dbf6d73SJordan K. Hubbard  		case SDT_MEMEAC:  /* memory execute only accessed conforming */
4220dbf6d73SJordan K. Hubbard  		case SDT_MEMERC:  /* memory execute read conforming */
4230dbf6d73SJordan K. Hubbard  		case SDT_MEMERAC: /* memory execute read accessed conforming */
4240dbf6d73SJordan K. Hubbard                          /* Must be "present" if executable and conforming. */
425da59a31cSDavid Greenman                          if (desc.sd.sd_p == 0)
426da59a31cSDavid Greenman                                  return (EACCES);
427da59a31cSDavid Greenman  			break;
4280dbf6d73SJordan K. Hubbard  		case SDT_MEMRO:   /* memory read only */
4290dbf6d73SJordan K. Hubbard  		case SDT_MEMROA:  /* memory read only accessed */
4300dbf6d73SJordan K. Hubbard  		case SDT_MEMRW:   /* memory read write */
4310dbf6d73SJordan K. Hubbard  		case SDT_MEMRWA:  /* memory read write accessed */
4320dbf6d73SJordan K. Hubbard  		case SDT_MEMROD:  /* memory read only expand dwn limit */
4330dbf6d73SJordan K. Hubbard  		case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
4340dbf6d73SJordan K. Hubbard  		case SDT_MEMRWD:  /* memory read write expand dwn limit */
4350dbf6d73SJordan K. Hubbard  		case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
4360dbf6d73SJordan K. Hubbard  		case SDT_MEME:    /* memory execute only */
4370dbf6d73SJordan K. Hubbard  		case SDT_MEMEA:   /* memory execute only accessed */
4380dbf6d73SJordan K. Hubbard  		case SDT_MEMER:   /* memory execute read */
4390dbf6d73SJordan K. Hubbard  		case SDT_MEMERA:  /* memory execute read accessed */
440da59a31cSDavid Greenman 			break;
441da59a31cSDavid Greenman 		default:
4420dbf6d73SJordan K. Hubbard 			return(EINVAL);
443da59a31cSDavid Greenman 			/*NOTREACHED*/
444da59a31cSDavid Greenman 		}
4450dbf6d73SJordan K. Hubbard 
4460dbf6d73SJordan K. Hubbard  		/* Only user (ring-3) descriptors may be present. */
4470dbf6d73SJordan K. Hubbard  		if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
4480dbf6d73SJordan K. Hubbard  			return (EACCES);
449da59a31cSDavid Greenman 	}
450da59a31cSDavid Greenman 
451da59a31cSDavid Greenman 	s = splhigh();
452da59a31cSDavid Greenman 
453da59a31cSDavid Greenman 	/* Fill in range */
4540dbf6d73SJordan K. Hubbard  	error = copyin(uap->desc,
4550dbf6d73SJordan K. Hubbard  		 &((union descriptor *)(pcb->pcb_ldt))[uap->start],
4560dbf6d73SJordan K. Hubbard  		uap->num * sizeof(union descriptor));
4570dbf6d73SJordan K. Hubbard  	if (!error)
458da59a31cSDavid Greenman   		*retval = uap->start;
459da59a31cSDavid Greenman 
460da59a31cSDavid Greenman 	splx(s);
461da59a31cSDavid Greenman 	return(error);
462da59a31cSDavid Greenman }
463da59a31cSDavid Greenman #endif	/* USER_LDT */
464