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