xref: /freebsd/sys/i386/i386/sys_machdep.c (revision b67dffda)
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
34b67dffdaSPeter Wemm  *	$Id: sys_machdep.c,v 1.26 1997/10/10 09:44:08 peter Exp $
35812a11a2SDavid Greenman  *
365b81b6b3SRodney W. Grimes  */
375b81b6b3SRodney W. Grimes 
38db6a20e2SGarrett Wollman #include "opt_user_ldt.h"
3998823b23SPeter Wemm #include "opt_vm86.h"
4098823b23SPeter Wemm 
41f540b106SGarrett Wollman #include <sys/param.h>
42f540b106SGarrett Wollman #include <sys/systm.h>
432f1ba63bSBruce Evans #include <sys/sysproto.h>
44f540b106SGarrett Wollman #include <sys/proc.h>
45efeaf95aSDavid Greenman 
46efeaf95aSDavid Greenman #include <vm/vm.h>
47996c772fSJohn Dyson #include <sys/lock.h>
48efeaf95aSDavid Greenman #include <vm/pmap.h>
49efeaf95aSDavid Greenman #include <vm/vm_map.h>
50efeaf95aSDavid Greenman #include <vm/vm_extern.h>
51efeaf95aSDavid Greenman 
52f540b106SGarrett Wollman #include <sys/user.h>
53812a11a2SDavid Greenman 
54f540b106SGarrett Wollman #include <machine/cpu.h>
55b67dffdaSPeter Wemm #include <machine/pcb_ext.h>	/* pcb.h included by sys/user.h */
56f540b106SGarrett Wollman #include <machine/sysarch.h>
57812a11a2SDavid Greenman 
58f540b106SGarrett Wollman #include <vm/vm_kern.h>		/* for kernel_map */
59da59a31cSDavid Greenman 
600dbf6d73SJordan K. Hubbard #define MAX_LD 8192
610dbf6d73SJordan K. Hubbard #define LD_PER_PAGE 512
620dbf6d73SJordan K. Hubbard #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
630dbf6d73SJordan K. Hubbard #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
640dbf6d73SJordan K. Hubbard 
650dbf6d73SJordan K. Hubbard 
660dbf6d73SJordan K. Hubbard 
67812a11a2SDavid Greenman void set_user_ldt	__P((struct pcb *pcb));
680b5b0f16SGarrett Wollman #ifdef USER_LDT
6987b91157SPoul-Henning Kamp static int i386_get_ldt	__P((struct proc *, char *, int *));
7087b91157SPoul-Henning Kamp static int i386_set_ldt	__P((struct proc *, char *, int *));
710b5b0f16SGarrett Wollman #endif
7248a09cf2SJohn Dyson #ifdef VM86
7348a09cf2SJohn Dyson static int i386_get_ioperm	__P((struct proc *, char *, int *));
7448a09cf2SJohn Dyson static int i386_set_ioperm	__P((struct proc *, char *, int *));
7548a09cf2SJohn Dyson int i386_extend_pcb	__P((struct proc *));
7648a09cf2SJohn Dyson #endif
775b81b6b3SRodney W. Grimes 
782f1ba63bSBruce Evans #ifndef _SYS_SYSPROTO_H_
79812a11a2SDavid Greenman struct sysarch_args {
80812a11a2SDavid Greenman 	int op;
81812a11a2SDavid Greenman 	char *parms;
8201ae5b20SDavid Greenman };
832f1ba63bSBruce Evans #endif
8401ae5b20SDavid Greenman 
85812a11a2SDavid Greenman int
86812a11a2SDavid Greenman sysarch(p, uap, retval)
8701ae5b20SDavid Greenman 	struct proc *p;
88812a11a2SDavid Greenman 	register struct sysarch_args *uap;
895b81b6b3SRodney W. Grimes 	int *retval;
905b81b6b3SRodney W. Grimes {
91812a11a2SDavid Greenman 	int error = 0;
925b81b6b3SRodney W. Grimes 
93812a11a2SDavid Greenman 	switch(uap->op) {
94812a11a2SDavid Greenman #ifdef	USER_LDT
95812a11a2SDavid Greenman 	case I386_GET_LDT:
96812a11a2SDavid Greenman 		error = i386_get_ldt(p, uap->parms, retval);
975b81b6b3SRodney W. Grimes 		break;
985b81b6b3SRodney W. Grimes 
99812a11a2SDavid Greenman 	case I386_SET_LDT:
100812a11a2SDavid Greenman 		error = i386_set_ldt(p, uap->parms, retval);
1015b81b6b3SRodney W. Grimes 		break;
1025b81b6b3SRodney W. Grimes #endif
10348a09cf2SJohn Dyson #ifdef VM86
10448a09cf2SJohn Dyson 	case I386_GET_IOPERM:
10548a09cf2SJohn Dyson 		error = i386_get_ioperm(p, uap->parms, retval);
10648a09cf2SJohn Dyson 		break;
10748a09cf2SJohn Dyson 	case I386_SET_IOPERM:
10848a09cf2SJohn Dyson 		error = i386_set_ioperm(p, uap->parms, retval);
10948a09cf2SJohn Dyson 		break;
11048a09cf2SJohn Dyson 	case I386_VM86:
1115f073933SJonathan Lemon 		error = vm86_sysarch(p, uap->parms, retval);
11248a09cf2SJohn Dyson 		break;
11348a09cf2SJohn Dyson #endif
114812a11a2SDavid Greenman 	default:
115812a11a2SDavid Greenman 		error = EINVAL;
116812a11a2SDavid Greenman 		break;
117812a11a2SDavid Greenman 	}
118812a11a2SDavid Greenman 	return (error);
119812a11a2SDavid Greenman }
120da59a31cSDavid Greenman 
12148a09cf2SJohn Dyson #ifdef VM86
12248a09cf2SJohn Dyson int
12348a09cf2SJohn Dyson i386_extend_pcb(struct proc *p)
12448a09cf2SJohn Dyson {
12548a09cf2SJohn Dyson 	int i, offset;
12648a09cf2SJohn Dyson 	u_long *addr;
12748a09cf2SJohn Dyson 	struct pcb_ext *ext;
12848a09cf2SJohn Dyson 	struct segment_descriptor sd;
12948a09cf2SJohn Dyson 	struct soft_segment_descriptor ssd = {
13048a09cf2SJohn Dyson 		0,			/* segment base address (overwritten) */
13148a09cf2SJohn Dyson 		ctob(IOPAGES + 1) - 1,	/* length */
13248a09cf2SJohn Dyson 		SDT_SYS386TSS,		/* segment type */
13348a09cf2SJohn Dyson 		0,			/* priority level */
13448a09cf2SJohn Dyson 		1,			/* descriptor present */
13548a09cf2SJohn Dyson 		0, 0,
13648a09cf2SJohn Dyson 		0,			/* default 32 size */
13748a09cf2SJohn Dyson 		0			/* granularity */
13848a09cf2SJohn Dyson 	};
13948a09cf2SJohn Dyson 
14048a09cf2SJohn Dyson 	ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
14148a09cf2SJohn Dyson 	if (ext == 0)
14248a09cf2SJohn Dyson 		return (ENOMEM);
14348a09cf2SJohn Dyson 	p->p_addr->u_pcb.pcb_ext = ext;
14448a09cf2SJohn Dyson 	bzero(&ext->ext_tss, sizeof(struct i386tss));
14548a09cf2SJohn Dyson 	ext->ext_tss.tss_esp0 = (unsigned)p->p_addr + ctob(UPAGES) - 16;
14648a09cf2SJohn Dyson         ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
14748a09cf2SJohn Dyson 	/*
14848a09cf2SJohn Dyson 	 * The last byte of the i/o map must be followed by an 0xff byte.
14948a09cf2SJohn Dyson 	 * We arbitrarily allocate 16 bytes here, to keep the starting
15048a09cf2SJohn Dyson 	 * address on a doubleword boundary.
15148a09cf2SJohn Dyson 	 */
15248a09cf2SJohn Dyson 	offset = PAGE_SIZE - 16;
15348a09cf2SJohn Dyson 	ext->ext_tss.tss_ioopt =
15448a09cf2SJohn Dyson 	    (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
15548a09cf2SJohn Dyson 	ext->ext_iomap = (caddr_t)ext + offset;
15648a09cf2SJohn Dyson 	ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
15748a09cf2SJohn Dyson 	ext->ext_vm86.vm86_inited = 0;
15848a09cf2SJohn Dyson 
15948a09cf2SJohn Dyson 	addr = (u_long *)ext->ext_vm86.vm86_intmap;
16048a09cf2SJohn Dyson 	for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
16148a09cf2SJohn Dyson 		*addr++ = ~0;
16248a09cf2SJohn Dyson 
16348a09cf2SJohn Dyson 	ssd.ssd_base = (unsigned)&ext->ext_tss;
16448a09cf2SJohn Dyson 	ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
16548a09cf2SJohn Dyson 	ssdtosd(&ssd, &ext->ext_tssd);
16648a09cf2SJohn Dyson 
16748a09cf2SJohn Dyson 	/* switch to the new TSS after syscall completes */
16848a09cf2SJohn Dyson 	need_resched();
16948a09cf2SJohn Dyson 
17048a09cf2SJohn Dyson 	return 0;
17148a09cf2SJohn Dyson }
17248a09cf2SJohn Dyson 
17348a09cf2SJohn Dyson struct i386_ioperm_args {
17448a09cf2SJohn Dyson 	u_short start;
17548a09cf2SJohn Dyson 	u_short length;
17648a09cf2SJohn Dyson 	u_char enable;
17748a09cf2SJohn Dyson };
17848a09cf2SJohn Dyson 
17948a09cf2SJohn Dyson static int
18048a09cf2SJohn Dyson i386_set_ioperm(p, args, retval)
18148a09cf2SJohn Dyson 	struct proc *p;
18248a09cf2SJohn Dyson 	char *args;
18348a09cf2SJohn Dyson 	int *retval;
18448a09cf2SJohn Dyson {
18548a09cf2SJohn Dyson 	int i, error = 0;
18648a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
18748a09cf2SJohn Dyson 	char *iomap;
18848a09cf2SJohn Dyson 
18948a09cf2SJohn Dyson 	if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args)))
19048a09cf2SJohn Dyson 		return (error);
19148a09cf2SJohn Dyson 
19248a09cf2SJohn Dyson         /* Only root can do this */
19348a09cf2SJohn Dyson         if (error = suser(p->p_ucred, &p->p_acflag))
19448a09cf2SJohn Dyson                 return (error);
19548a09cf2SJohn Dyson 	/*
19648a09cf2SJohn Dyson 	 * XXX
19748a09cf2SJohn Dyson 	 * While this is restricted to root, we should probably figure out
19848a09cf2SJohn Dyson 	 * whether any other driver is using this i/o address, as so not to
19948a09cf2SJohn Dyson 	 * cause confusion.  This probably requires a global 'usage registry'.
20048a09cf2SJohn Dyson 	 */
20148a09cf2SJohn Dyson 
20248a09cf2SJohn Dyson 	if (p->p_addr->u_pcb.pcb_ext == 0)
20348a09cf2SJohn Dyson 		if (error = i386_extend_pcb(p))
20448a09cf2SJohn Dyson 			return (error);
20548a09cf2SJohn Dyson 	iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
20648a09cf2SJohn Dyson 
20748a09cf2SJohn Dyson 	if ((int)(ua.start + ua.length) > 0xffff)
20848a09cf2SJohn Dyson 		return (EINVAL);
20948a09cf2SJohn Dyson 
21048a09cf2SJohn Dyson 	for (i = ua.start; i < (int)(ua.start + ua.length) + 1; i++) {
21148a09cf2SJohn Dyson 		if (ua.enable)
21248a09cf2SJohn Dyson 			iomap[i >> 3] &= ~(1 << (i & 7));
21348a09cf2SJohn Dyson 		else
21448a09cf2SJohn Dyson 			iomap[i >> 3] |= (1 << (i & 7));
21548a09cf2SJohn Dyson 	}
21648a09cf2SJohn Dyson 	return (error);
21748a09cf2SJohn Dyson }
21848a09cf2SJohn Dyson 
21948a09cf2SJohn Dyson static int
22048a09cf2SJohn Dyson i386_get_ioperm(p, args, retval)
22148a09cf2SJohn Dyson 	struct proc *p;
22248a09cf2SJohn Dyson 	char *args;
22348a09cf2SJohn Dyson 	int *retval;
22448a09cf2SJohn Dyson {
22548a09cf2SJohn Dyson 	int i, state, error = 0;
22648a09cf2SJohn Dyson 	struct i386_ioperm_args ua;
22748a09cf2SJohn Dyson 	char *iomap;
22848a09cf2SJohn Dyson 
22948a09cf2SJohn Dyson 	if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args)))
23048a09cf2SJohn Dyson 		return (error);
23148a09cf2SJohn Dyson 
23248a09cf2SJohn Dyson 	if (p->p_addr->u_pcb.pcb_ext == 0) {
23348a09cf2SJohn Dyson 		ua.length = 0;
23448a09cf2SJohn Dyson 		goto done;
23548a09cf2SJohn Dyson 	}
23648a09cf2SJohn Dyson 
23748a09cf2SJohn Dyson 	iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
23848a09cf2SJohn Dyson 
23948a09cf2SJohn Dyson 	state = (iomap[i >> 3] >> (i & 7)) & 1;
24048a09cf2SJohn Dyson 	ua.enable = !state;
24148a09cf2SJohn Dyson 	ua.length = 1;
24248a09cf2SJohn Dyson 
24348a09cf2SJohn Dyson 	for (i = ua.start + 1; i < 0x10000; i++) {
24448a09cf2SJohn Dyson 		if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
24548a09cf2SJohn Dyson 			break;
24648a09cf2SJohn Dyson 		ua.length++;
24748a09cf2SJohn Dyson 	}
24848a09cf2SJohn Dyson 
24948a09cf2SJohn Dyson done:
25048a09cf2SJohn Dyson 	error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
25148a09cf2SJohn Dyson 	return (error);
25248a09cf2SJohn Dyson }
25348a09cf2SJohn Dyson #endif /* VM86 */
25448a09cf2SJohn Dyson 
255da59a31cSDavid Greenman #ifdef USER_LDT
2560dbf6d73SJordan K. Hubbard /*
2570dbf6d73SJordan K. Hubbard  * Update the GDT entry pointing to the LDT to point to the LDT of the
2580dbf6d73SJordan K. Hubbard  * current process.
2590dbf6d73SJordan K. Hubbard  */
260da59a31cSDavid Greenman void
261da59a31cSDavid Greenman set_user_ldt(struct pcb *pcb)
262da59a31cSDavid Greenman {
263da59a31cSDavid Greenman 	gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt;
264da59a31cSDavid Greenman 	gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1;
2656d520d66SBruce Evans 	ssdtosd(&gdt_segs[GUSERLDT_SEL], &gdt[GUSERLDT_SEL].sd);
266da59a31cSDavid Greenman 	lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
267da59a31cSDavid Greenman 	currentldt = GSEL(GUSERLDT_SEL, SEL_KPL);
268da59a31cSDavid Greenman }
269da59a31cSDavid Greenman 
270da59a31cSDavid Greenman struct i386_get_ldt_args {
271da59a31cSDavid Greenman 	int start;
272da59a31cSDavid Greenman 	union descriptor *desc;
273da59a31cSDavid Greenman 	int num;
274da59a31cSDavid Greenman };
275da59a31cSDavid Greenman 
27687b91157SPoul-Henning Kamp static int
277da59a31cSDavid Greenman i386_get_ldt(p, args, retval)
278da59a31cSDavid Greenman 	struct proc *p;
279da59a31cSDavid Greenman 	char *args;
280da59a31cSDavid Greenman 	int *retval;
281da59a31cSDavid Greenman {
282da59a31cSDavid Greenman 	int error = 0;
283da59a31cSDavid Greenman 	struct pcb *pcb = &p->p_addr->u_pcb;
284da59a31cSDavid Greenman 	int nldt, num;
285da59a31cSDavid Greenman 	union descriptor *lp;
286da59a31cSDavid Greenman 	int s;
2870dbf6d73SJordan K. Hubbard 	struct i386_get_ldt_args ua;
2880dbf6d73SJordan K. Hubbard 	struct i386_get_ldt_args *uap = &ua;
289da59a31cSDavid Greenman 
2900dbf6d73SJordan K. Hubbard 	if ((error = copyin(args, uap, sizeof(struct i386_get_ldt_args))) < 0)
291da59a31cSDavid Greenman 		return(error);
292da59a31cSDavid Greenman 
293da59a31cSDavid Greenman #ifdef	DEBUG
2940dbf6d73SJordan K. Hubbard 	printf("i386_get_ldt: start=%d num=%d descs=%x\n", uap->start,
2950dbf6d73SJordan K. Hubbard 		uap->num, uap->desc);
296da59a31cSDavid Greenman #endif
297da59a31cSDavid Greenman 
2980dbf6d73SJordan K. Hubbard 	/* verify range of LDTs exist */
2990dbf6d73SJordan K. Hubbard 	if ((uap->start < 0) || (uap->num <= 0))
300da59a31cSDavid Greenman 		return(EINVAL);
301da59a31cSDavid Greenman 
302da59a31cSDavid Greenman 	s = splhigh();
303da59a31cSDavid Greenman 
304da59a31cSDavid Greenman 	if (pcb->pcb_ldt) {
305da59a31cSDavid Greenman 		nldt = pcb->pcb_ldt_len;
306da59a31cSDavid Greenman 		num = min(uap->num, nldt);
307da59a31cSDavid Greenman 		lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start];
308da59a31cSDavid Greenman 	} else {
309da59a31cSDavid Greenman 		nldt = sizeof(ldt)/sizeof(ldt[0]);
310da59a31cSDavid Greenman 		num = min(uap->num, nldt);
311da59a31cSDavid Greenman 		lp = &ldt[uap->start];
312da59a31cSDavid Greenman 	}
313da59a31cSDavid Greenman 	if (uap->start > nldt) {
314da59a31cSDavid Greenman 		splx(s);
315da59a31cSDavid Greenman 		return(EINVAL);
316da59a31cSDavid Greenman 	}
317da59a31cSDavid Greenman 
318da59a31cSDavid Greenman 	error = copyout(lp, uap->desc, num * sizeof(union descriptor));
319da59a31cSDavid Greenman 	if (!error)
320da59a31cSDavid Greenman 		*retval = num;
321da59a31cSDavid Greenman 
322da59a31cSDavid Greenman 	splx(s);
323da59a31cSDavid Greenman 	return(error);
324da59a31cSDavid Greenman }
325da59a31cSDavid Greenman 
326da59a31cSDavid Greenman struct i386_set_ldt_args {
327da59a31cSDavid Greenman 	int start;
328da59a31cSDavid Greenman 	union descriptor *desc;
329da59a31cSDavid Greenman 	int num;
330da59a31cSDavid Greenman };
331da59a31cSDavid Greenman 
33287b91157SPoul-Henning Kamp static int
333da59a31cSDavid Greenman i386_set_ldt(p, args, retval)
334da59a31cSDavid Greenman 	struct proc *p;
335da59a31cSDavid Greenman 	char *args;
336da59a31cSDavid Greenman 	int *retval;
337da59a31cSDavid Greenman {
338da59a31cSDavid Greenman 	int error = 0, i, n;
3390dbf6d73SJordan K. Hubbard  	int largest_ld;
340da59a31cSDavid Greenman 	struct pcb *pcb = &p->p_addr->u_pcb;
341da59a31cSDavid Greenman 	int s;
342da59a31cSDavid Greenman 	struct i386_set_ldt_args ua, *uap;
343da59a31cSDavid Greenman 
344da59a31cSDavid Greenman 	if ((error = copyin(args, &ua, sizeof(struct i386_set_ldt_args))) < 0)
345da59a31cSDavid Greenman 		return(error);
346da59a31cSDavid Greenman 
347da59a31cSDavid Greenman 	uap = &ua;
348da59a31cSDavid Greenman 
349da59a31cSDavid Greenman #ifdef	DEBUG
350da59a31cSDavid Greenman 	printf("i386_set_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc);
351da59a31cSDavid Greenman #endif
352da59a31cSDavid Greenman 
3530dbf6d73SJordan K. Hubbard  	/* verify range of descriptors to modify */
3548bb9c4c7SSteven Wallace  	if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
3550dbf6d73SJordan K. Hubbard  		(uap->num > MAX_LD))
3560dbf6d73SJordan K. Hubbard  	{
357da59a31cSDavid Greenman  		return(EINVAL);
3580dbf6d73SJordan K. Hubbard  	}
3590dbf6d73SJordan K. Hubbard  	largest_ld = uap->start + uap->num - 1;
3600dbf6d73SJordan K. Hubbard  	if (largest_ld >= MAX_LD)
361da59a31cSDavid Greenman   		return(EINVAL);
362da59a31cSDavid Greenman 
363da59a31cSDavid Greenman   	/* allocate user ldt */
3640dbf6d73SJordan K. Hubbard  	if (!pcb->pcb_ldt || (largest_ld >= pcb->pcb_ldt_len)) {
3650dbf6d73SJordan K. Hubbard  		union descriptor *new_ldt = (union descriptor *)kmem_alloc(
3660dbf6d73SJordan K. Hubbard  			kernel_map, SIZE_FROM_LARGEST_LD(largest_ld));
3670dbf6d73SJordan K. Hubbard  		if (new_ldt == NULL) {
3680dbf6d73SJordan K. Hubbard  			return ENOMEM;
3690dbf6d73SJordan K. Hubbard  		}
3700dbf6d73SJordan K. Hubbard  		if (pcb->pcb_ldt) {
3710dbf6d73SJordan K. Hubbard  			bcopy(pcb->pcb_ldt, new_ldt, pcb->pcb_ldt_len
3720dbf6d73SJordan K. Hubbard  				* sizeof(union descriptor));
3730dbf6d73SJordan K. Hubbard  			kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
3740dbf6d73SJordan K. Hubbard  				pcb->pcb_ldt_len * sizeof(union descriptor));
3750dbf6d73SJordan K. Hubbard  		} else {
376da59a31cSDavid Greenman  			bcopy(ldt, new_ldt, sizeof(ldt));
3770dbf6d73SJordan K. Hubbard  		}
378da59a31cSDavid Greenman   		pcb->pcb_ldt = (caddr_t)new_ldt;
3790dbf6d73SJordan K. Hubbard  		pcb->pcb_ldt_len = NEW_MAX_LD(largest_ld);
3800dbf6d73SJordan K. Hubbard  		if (pcb == curpcb)
3810dbf6d73SJordan K. Hubbard  		    set_user_ldt(pcb);
382da59a31cSDavid Greenman   	}
383da59a31cSDavid Greenman 
384da59a31cSDavid Greenman 	/* Check descriptors for access violations */
385da59a31cSDavid Greenman 	for (i = 0, n = uap->start; i < uap->num; i++, n++) {
386da59a31cSDavid Greenman 		union descriptor desc, *dp;
387da59a31cSDavid Greenman 		dp = &uap->desc[i];
388da59a31cSDavid Greenman 		error = copyin(dp, &desc, sizeof(union descriptor));
389da59a31cSDavid Greenman 		if (error)
390da59a31cSDavid Greenman 			return(error);
391da59a31cSDavid Greenman 
3920dbf6d73SJordan K. Hubbard 		switch (desc.sd.sd_type) {
3930dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL:	/* system null */
3940dbf6d73SJordan K. Hubbard  			desc.sd.sd_p = 0;
3950dbf6d73SJordan K. Hubbard   			break;
3960dbf6d73SJordan K. Hubbard  		case SDT_SYS286TSS: /* system 286 TSS available */
3970dbf6d73SJordan K. Hubbard  		case SDT_SYSLDT:    /* system local descriptor table */
3980dbf6d73SJordan K. Hubbard  		case SDT_SYS286BSY: /* system 286 TSS busy */
3990dbf6d73SJordan K. Hubbard  		case SDT_SYSTASKGT: /* system task gate */
4000dbf6d73SJordan K. Hubbard  		case SDT_SYS286IGT: /* system 286 interrupt gate */
4010dbf6d73SJordan K. Hubbard  		case SDT_SYS286TGT: /* system 286 trap gate */
4020dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL2:  /* undefined by Intel */
4030dbf6d73SJordan K. Hubbard  		case SDT_SYS386TSS: /* system 386 TSS available */
4040dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL3:  /* undefined by Intel */
4050dbf6d73SJordan K. Hubbard  		case SDT_SYS386BSY: /* system 386 TSS busy */
4060dbf6d73SJordan K. Hubbard  		case SDT_SYSNULL4:  /* undefined by Intel */
4070dbf6d73SJordan K. Hubbard  		case SDT_SYS386IGT: /* system 386 interrupt gate */
4080dbf6d73SJordan K. Hubbard  		case SDT_SYS386TGT: /* system 386 trap gate */
4090dbf6d73SJordan K. Hubbard  		case SDT_SYS286CGT: /* system 286 call gate */
4100dbf6d73SJordan K. Hubbard  		case SDT_SYS386CGT: /* system 386 call gate */
4110dbf6d73SJordan K. Hubbard  			/* I can't think of any reason to allow a user proc
4120dbf6d73SJordan K. Hubbard  			 * to create a segment of these types.  They are
4130dbf6d73SJordan K. Hubbard  			 * for OS use only.
4140dbf6d73SJordan K. Hubbard  			 */
4150dbf6d73SJordan K. Hubbard      	    	    	return EACCES;
416da59a31cSDavid Greenman 
4170dbf6d73SJordan K. Hubbard  		/* memory segment types */
4180dbf6d73SJordan K. Hubbard  		case SDT_MEMEC:   /* memory execute only conforming */
4190dbf6d73SJordan K. Hubbard  		case SDT_MEMEAC:  /* memory execute only accessed conforming */
4200dbf6d73SJordan K. Hubbard  		case SDT_MEMERC:  /* memory execute read conforming */
4210dbf6d73SJordan K. Hubbard  		case SDT_MEMERAC: /* memory execute read accessed conforming */
4220dbf6d73SJordan K. Hubbard                          /* Must be "present" if executable and conforming. */
423da59a31cSDavid Greenman                          if (desc.sd.sd_p == 0)
424da59a31cSDavid Greenman                                  return (EACCES);
425da59a31cSDavid Greenman  			break;
4260dbf6d73SJordan K. Hubbard  		case SDT_MEMRO:   /* memory read only */
4270dbf6d73SJordan K. Hubbard  		case SDT_MEMROA:  /* memory read only accessed */
4280dbf6d73SJordan K. Hubbard  		case SDT_MEMRW:   /* memory read write */
4290dbf6d73SJordan K. Hubbard  		case SDT_MEMRWA:  /* memory read write accessed */
4300dbf6d73SJordan K. Hubbard  		case SDT_MEMROD:  /* memory read only expand dwn limit */
4310dbf6d73SJordan K. Hubbard  		case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
4320dbf6d73SJordan K. Hubbard  		case SDT_MEMRWD:  /* memory read write expand dwn limit */
4330dbf6d73SJordan K. Hubbard  		case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
4340dbf6d73SJordan K. Hubbard  		case SDT_MEME:    /* memory execute only */
4350dbf6d73SJordan K. Hubbard  		case SDT_MEMEA:   /* memory execute only accessed */
4360dbf6d73SJordan K. Hubbard  		case SDT_MEMER:   /* memory execute read */
4370dbf6d73SJordan K. Hubbard  		case SDT_MEMERA:  /* memory execute read accessed */
438da59a31cSDavid Greenman 			break;
439da59a31cSDavid Greenman 		default:
4400dbf6d73SJordan K. Hubbard 			return(EINVAL);
441da59a31cSDavid Greenman 			/*NOTREACHED*/
442da59a31cSDavid Greenman 		}
4430dbf6d73SJordan K. Hubbard 
4440dbf6d73SJordan K. Hubbard  		/* Only user (ring-3) descriptors may be present. */
4450dbf6d73SJordan K. Hubbard  		if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
4460dbf6d73SJordan K. Hubbard  			return (EACCES);
447da59a31cSDavid Greenman 	}
448da59a31cSDavid Greenman 
449da59a31cSDavid Greenman 	s = splhigh();
450da59a31cSDavid Greenman 
451da59a31cSDavid Greenman 	/* Fill in range */
4520dbf6d73SJordan K. Hubbard  	error = copyin(uap->desc,
4530dbf6d73SJordan K. Hubbard  		 &((union descriptor *)(pcb->pcb_ldt))[uap->start],
4540dbf6d73SJordan K. Hubbard  		uap->num * sizeof(union descriptor));
4550dbf6d73SJordan K. Hubbard  	if (!error)
456da59a31cSDavid Greenman   		*retval = uap->start;
457da59a31cSDavid Greenman 
458da59a31cSDavid Greenman 	splx(s);
459da59a31cSDavid Greenman 	return(error);
460da59a31cSDavid Greenman }
461da59a31cSDavid Greenman #endif	/* USER_LDT */
462