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