diff options
author | Alexander Yurchenko <grange@cvs.openbsd.org> | 2004-02-01 12:26:46 +0000 |
---|---|---|
committer | Alexander Yurchenko <grange@cvs.openbsd.org> | 2004-02-01 12:26:46 +0000 |
commit | c8cf656fed7c9f843495fd1f13b9514aaa40461c (patch) | |
tree | c3c3578dae317ff1d146a10ae50487fe8f28a6c9 /sys/arch/i386 | |
parent | 88fc05d254d5bf5b952ce664fd7f7dcc302187db (diff) |
Sync user ldt code with NetBSD:
- finally remove it from pcb, it's a pmap thing only
- more sanity checks
- better lockin
- may be something else
Fixes panics when using apps requiring it (mplayer-win32 e.g.).
Problem found and test espie@.
OKs from miod@ (sshhh, don't tell anyone) and art@.
Diffstat (limited to 'sys/arch/i386')
-rw-r--r-- | sys/arch/i386/i386/machdep.c | 8 | ||||
-rw-r--r-- | sys/arch/i386/i386/pmap.c | 5 | ||||
-rw-r--r-- | sys/arch/i386/i386/sys_machdep.c | 275 | ||||
-rw-r--r-- | sys/arch/i386/include/cpu.h | 3 | ||||
-rw-r--r-- | sys/arch/i386/include/pcb.h | 4 |
5 files changed, 159 insertions, 136 deletions
diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index ea177f7b693..04ce6163d49 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.266 2004/01/31 00:09:41 deraadt Exp $ */ +/* $OpenBSD: machdep.c,v 1.267 2004/02/01 12:26:45 grange Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -454,7 +454,6 @@ i386_proc0_tss_ldt_init() int x; curpcb = pcb = &proc0.p_addr->u_pcb; - pcb->pcb_flags = 0; pcb->pcb_tss.tss_ioopt = ((caddr_t)pcb->pcb_iomap - (caddr_t)&pcb->pcb_tss) << 16; for (x = 0; x < sizeof(pcb->pcb_iomap) / 4; x++) @@ -2618,7 +2617,6 @@ setregs(p, pack, stack, retval) u_long stack; register_t *retval; { - struct pcb *pcb = &p->p_addr->u_pcb; struct pmap *pmap = vm_map_pmap(&p->p_vmspace->vm_map); struct trapframe *tf = p->p_md.md_regs; @@ -2629,12 +2627,10 @@ setregs(p, pack, stack, retval) #endif #ifdef USER_LDT - if (pcb->pcb_flags & PCB_USER_LDT) - i386_user_cleanup(pcb); + pmap_ldt_cleanup(p); #endif p->p_md.md_flags &= ~MDP_USEDFPU; - pcb->pcb_flags = 0; __asm("movw %w0,%%gs" : : "r" (LSEL(LUDATA_SEL, SEL_UPL))); __asm("movw %w0,%%fs" : : "r" (LSEL(LUDATA_SEL, SEL_UPL))); diff --git a/sys/arch/i386/i386/pmap.c b/sys/arch/i386/i386/pmap.c index 622827a5879..8dc8115009e 100644 --- a/sys/arch/i386/i386/pmap.c +++ b/sys/arch/i386/i386/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.74 2004/01/29 19:01:54 tedu Exp $ */ +/* $OpenBSD: pmap.c,v 1.75 2004/02/01 12:26:45 grange Exp $ */ /* $NetBSD: pmap.c,v 1.91 2000/06/02 17:46:37 thorpej Exp $ */ /* @@ -1881,6 +1881,9 @@ pmap_release(pmap) /* * no need to switch the LDT; this address space is gone, * nothing is using it. + * + * No need to lock the pmap for ldt_free (or anything else), + * we're the last one to use it. */ ldt_free(pmap); uvm_km_free(kernel_map, (vaddr_t)pmap->pm_ldt, diff --git a/sys/arch/i386/i386/sys_machdep.c b/sys/arch/i386/i386/sys_machdep.c index fa12399a187..d423a40f040 100644 --- a/sys/arch/i386/i386/sys_machdep.c +++ b/sys/arch/i386/i386/sys_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_machdep.c,v 1.22 2003/08/15 20:32:13 tedu Exp $ */ +/* $OpenBSD: sys_machdep.c,v 1.23 2004/02/01 12:26:45 grange Exp $ */ /* $NetBSD: sys_machdep.c,v 1.28 1996/05/03 19:42:29 christos Exp $ */ /*- @@ -49,6 +49,7 @@ #include <sys/mtio.h> #include <sys/buf.h> #include <sys/signal.h> +#include <sys/malloc.h> #include <sys/mount.h> #include <sys/syscallargs.h> @@ -68,32 +69,26 @@ extern struct vm_map *kernel_map; -#ifdef USER_LDT -int i386_get_ldt(struct proc *, void *, register_t *); -int i386_set_ldt(struct proc *, void *, register_t *); -#endif int i386_iopl(struct proc *, void *, register_t *); int i386_get_ioperm(struct proc *, void *, register_t *); int i386_set_ioperm(struct proc *, void *, register_t *); #ifdef USER_LDT -/* - * If the process has a local LDT, deallocate it, and restore the default from - * proc0. - */ -void -i386_user_cleanup(pcb) - struct pcb *pcb; -{ - ldt_free(pcb->pcb_pmap); - pcb->pcb_ldt_sel = GSEL(GLDT_SEL, SEL_KPL); - if (pcb == curpcb) - lldt(pcb->pcb_ldt_sel); - uvm_km_free(kernel_map, (vaddr_t)pcb->pcb_ldt, - (pcb->pcb_ldt_len * sizeof(union descriptor))); - pcb->pcb_ldt = 0; +#ifdef LDT_DEBUG +static void i386_print_ldt(int, const struct segment_descriptor *); + +static void +i386_print_ldt(i, d) + int i; + const struct segment_descriptor *d; +{ + printf("[%d] lolimit=0x%x, lobase=0x%x, type=%u, dpl=%u, p=%u, " + "hilimit=0x%x, xx=%x, def32=%u, gran=%u, hibase=0x%x\n", + i, d->sd_lolimit, d->sd_lobase, d->sd_type, d->sd_dpl, d->sd_p, + d->sd_hilimit, d->sd_xx, d->sd_def32, d->sd_gran, d->sd_hibase); } +#endif int i386_get_ldt(p, args, retval) @@ -102,9 +97,9 @@ i386_get_ldt(p, args, retval) register_t *retval; { int error; - struct pcb *pcb = &p->p_addr->u_pcb; + pmap_t pmap = p->p_vmspace->vm_map.pmap; int nldt, num; - union descriptor *lp; + union descriptor *lp, *cp; struct i386_get_ldt_args ua; if (user_ldt_enable == 0) @@ -113,34 +108,54 @@ i386_get_ldt(p, args, retval) if ((error = copyin(args, &ua, sizeof(ua))) != 0) return (error); -#ifdef LDTDEBUG +#ifdef LDT_DEBUG printf("i386_get_ldt: start=%d num=%d descs=%p\n", ua.start, ua.num, ua.desc); #endif - if (ua.start < 0 || ua.num < 0) + if (ua.start < 0 || ua.num < 0 || ua.start > 8192 || ua.num > 8192 || + ua.start + ua.num > 8192) return (EINVAL); - if (pcb->pcb_flags & PCB_USER_LDT) { - nldt = pcb->pcb_ldt_len; - lp = pcb->pcb_ldt; + cp = malloc(ua.num * sizeof(union descriptor), M_TEMP, M_WAITOK); + if (cp == NULL) + return ENOMEM; + + simple_lock(&pmap->pm_lock); + + if (pmap->pm_flags & PMF_USER_LDT) { + nldt = pmap->pm_ldt_len; + lp = pmap->pm_ldt; } else { nldt = NLDT; lp = ldt; } - if (ua.start > nldt) + if (ua.start > nldt) { + simple_unlock(&pmap->pm_lock); + free(cp, M_TEMP); return (EINVAL); + } lp += ua.start; num = min(ua.num, nldt - ua.start); +#ifdef LDT_DEBUG + { + int i; + for (i = 0; i < num; i++) + i386_print_ldt(i, &lp[i].sd); + } +#endif - error = copyout(lp, ua.desc, num * sizeof(union descriptor)); - if (error) - return (error); + memcpy(cp, lp, num * sizeof(union descriptor)); + simple_unlock(&pmap->pm_lock); - *retval = num; - return (0); + error = copyout(cp, ua.desc, num * sizeof(union descriptor)); + if (error == 0) + *retval = num; + + free(cp, M_TEMP); + return (error); } int @@ -152,9 +167,10 @@ i386_set_ldt(p, args, retval) int error, i, n; struct pcb *pcb = &p->p_addr->u_pcb; pmap_t pmap = p->p_vmspace->vm_map.pmap; - int fsslot, gsslot; struct i386_set_ldt_args ua; - union descriptor desc; + union descriptor *descv; + size_t old_len, new_len, ldt_len; + union descriptor *old_ldt, *new_ldt; if (user_ldt_enable == 0) return (ENOSYS); @@ -162,77 +178,24 @@ i386_set_ldt(p, args, retval) if ((error = copyin(args, &ua, sizeof(ua))) != 0) return (error); -#ifdef LDT_DEBUG - printf("i386_set_ldt: start=%d num=%d descs=%p\n", ua.start, - ua.num, ua.desc); -#endif - - if (ua.start < 0 || ua.num < 0 || - ua.start > 8192 || ua.num > 8192 || - (ua.start + ua.num) > 8192) + if (ua.start < 0 || ua.num < 0 || ua.start > 8192 || ua.num > 8192 || + ua.start + ua.num > 8192) return (EINVAL); - /* - * XXX LOCKING - */ - - /* allocate user ldt */ - if (pmap->pm_ldt == 0 || (ua.start + ua.num) > pmap->pm_ldt_len) { - size_t old_len, new_len; - union descriptor *old_ldt, *new_ldt; - - if (pmap->pm_flags & PMF_USER_LDT) { - old_len = pmap->pm_ldt_len * sizeof(union descriptor); - old_ldt = pmap->pm_ldt; - } else { - old_len = NLDT * sizeof(union descriptor); - old_ldt = ldt; - pmap->pm_ldt_len = 512; - } - while ((ua.start + ua.num) > pmap->pm_ldt_len) - pmap->pm_ldt_len *= 2; - new_len = pmap->pm_ldt_len * sizeof(union descriptor); - new_ldt = (union descriptor *)uvm_km_alloc(kernel_map, new_len); - bcopy(old_ldt, new_ldt, old_len); - bzero((caddr_t)new_ldt + old_len, new_len - old_len); - pmap->pm_ldt = new_ldt; - - if (pmap->pm_flags & PMF_USER_LDT) - ldt_free(pmap); - else - pmap->pm_flags |= PMF_USER_LDT; - ldt_alloc(pmap, new_ldt, new_len); - pcb->pcb_ldt_sel = pmap->pm_ldt_sel; - if (pcb == curpcb) - lldt(pcb->pcb_ldt_sel); - - /* - * XXX Need to notify other processors which may be - * XXX currently using this pmap that they need to - * XXX re-load the LDT. - */ + descv = malloc(sizeof (*descv) * ua.num, M_TEMP, M_NOWAIT); + if (descv == NULL) + return (ENOMEM); - if (old_ldt != ldt) - uvm_km_free(kernel_map, (vaddr_t)old_ldt, old_len); -#ifdef LDT_DEBUG - printf("i386_set_ldt(%d): new_ldt=%p\n", p->p_pid, new_ldt); -#endif - } - - if (pcb == curpcb) - savectx(curpcb); - fsslot = IDXSEL(pcb->pcb_fs); - gsslot = IDXSEL(pcb->pcb_gs); - error = 0; + if ((error = copyin(ua.desc, descv, sizeof (*descv) * ua.num)) != 0) + goto out; /* Check descriptors for access violations. */ - for (i = 0, n = ua.start; i < ua.num; i++, n++) { - if ((error = copyin(&ua.desc[i], &desc, sizeof(desc))) != 0) - return (error); + for (i = 0; i < ua.num; i++) { + union descriptor *desc = &descv[i]; - switch (desc.sd.sd_type) { + switch (desc->sd.sd_type) { case SDT_SYSNULL: - desc.sd.sd_p = 0; + desc->sd.sd_p = 0; break; case SDT_SYS286CGT: case SDT_SYS386CGT: @@ -242,22 +205,24 @@ i386_set_ldt(p, args, retval) * part of the gdt. Segments in the LDT are * constrained (below) to be user segments. */ - if (desc.gd.gd_p != 0 && !ISLDT(desc.gd.gd_selector) && - ((IDXSEL(desc.gd.gd_selector) >= NGDT) || - (gdt[IDXSEL(desc.gd.gd_selector)].sd.sd_dpl != - SEL_UPL))) - return (EACCES); - /* Can't replace in use descriptor with gate. */ - if (n == fsslot || n == gsslot) - return (EBUSY); + if (desc->gd.gd_p != 0 && + !ISLDT(desc->gd.gd_selector) && + ((IDXSEL(desc->gd.gd_selector) >= NGDT) || + (gdt[IDXSEL(desc->gd.gd_selector)].sd.sd_dpl != + SEL_UPL))) { + error = EACCES; + goto out; + } break; case SDT_MEMEC: case SDT_MEMEAC: case SDT_MEMERC: case SDT_MEMERAC: /* Must be "present" if executable and conforming. */ - if (desc.sd.sd_p == 0) - return (EACCES); + if (desc->sd.sd_p == 0) { + error = EACCES; + goto out; + } break; case SDT_MEMRO: case SDT_MEMROA: @@ -265,40 +230,102 @@ i386_set_ldt(p, args, retval) case SDT_MEMRWA: case SDT_MEMROD: case SDT_MEMRODA: + case SDT_MEMRWD: + case SDT_MEMRWDA: case SDT_MEME: case SDT_MEMEA: case SDT_MEMER: case SDT_MEMERA: break; default: - /* Only care if it's present. */ - if (desc.sd.sd_p != 0) - return (EACCES); + /* + * Make sure that unknown descriptor types are + * not marked present. + */ + if (desc->sd.sd_p != 0) { + error = EACCES; + goto out; + } break; } - if (desc.sd.sd_p != 0) { + if (desc->sd.sd_p != 0) { /* Only user (ring-3) descriptors may be present. */ - if (desc.sd.sd_dpl != SEL_UPL) - return (EACCES); - } else { - /* Must be "present" if in use. */ - if (n == fsslot || n == gsslot) - return (EBUSY); + if (desc->sd.sd_dpl != SEL_UPL) { + error = EACCES; + goto out; + } } } - /* Now actually replace the descriptors. */ - for (i = 0, n = ua.start; i < ua.num; i++, n++) { - if ((error = copyin(&ua.desc[i], &desc, sizeof(desc))) != 0) - goto out; + /* allocate user ldt */ + simple_lock(&pmap->pm_lock); + if (pmap->pm_ldt == 0 || (ua.start + ua.num) > pmap->pm_ldt_len) { + if (pmap->pm_flags & PMF_USER_LDT) + ldt_len = pmap->pm_ldt_len; + else + ldt_len = 512; + while ((ua.start + ua.num) > ldt_len) + ldt_len *= 2; + new_len = ldt_len * sizeof(union descriptor); + + simple_unlock(&pmap->pm_lock); + new_ldt = (union descriptor *)uvm_km_alloc(kernel_map, + new_len); + simple_lock(&pmap->pm_lock); + + if (pmap->pm_ldt != NULL && ldt_len <= pmap->pm_ldt_len) { + /* + * Another thread (re)allocated the LDT to + * sufficient size while we were blocked in + * uvm_km_alloc. Oh well. The new entries + * will quite probably not be right, but + * hey.. not our problem if user applications + * have race conditions like that. + */ + uvm_km_free(kernel_map, (vaddr_t)new_ldt, new_len); + goto copy; + } + + old_ldt = pmap->pm_ldt; + + if (old_ldt != NULL) { + old_len = pmap->pm_ldt_len * sizeof(union descriptor); + } else { + old_len = NLDT * sizeof(union descriptor); + old_ldt = ldt; + } + + memcpy(new_ldt, old_ldt, old_len); + memset((caddr_t)new_ldt + old_len, 0, new_len - old_len); + + if (old_ldt != ldt) + uvm_km_free(kernel_map, (vaddr_t)old_ldt, old_len); + + pmap->pm_ldt = new_ldt; + pmap->pm_ldt_len = ldt_len; + + if (pmap->pm_flags & PMF_USER_LDT) + ldt_free(pmap); + else + pmap->pm_flags |= PMF_USER_LDT; + ldt_alloc(pmap, new_ldt, new_len); + pcb->pcb_ldt_sel = pmap->pm_ldt_sel; + if (pcb == curpcb) + lldt(pcb->pcb_ldt_sel); - pmap->pm_ldt[n] = desc; } +copy: + /* Now actually replace the descriptors. */ + for (i = 0, n = ua.start; i < ua.num; i++, n++) + pmap->pm_ldt[n] = descv[i]; + + simple_unlock(&pmap->pm_lock); *retval = ua.start; out: + free(descv, M_TEMP); return (error); } #endif /* USER_LDT */ diff --git a/sys/arch/i386/include/cpu.h b/sys/arch/i386/include/cpu.h index 39fafba22de..2750fabdda6 100644 --- a/sys/arch/i386/include/cpu.h +++ b/sys/arch/i386/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.55 2004/01/06 21:09:20 tedu Exp $ */ +/* $OpenBSD: cpu.h,v 1.56 2004/02/01 12:26:45 grange Exp $ */ /* $NetBSD: cpu.h,v 1.35 1996/05/05 19:29:26 christos Exp $ */ /*- @@ -216,7 +216,6 @@ int math_emulate(struct trapframe *); #ifdef USER_LDT /* sys_machdep.h */ extern int user_ldt_enable; -void i386_user_cleanup(struct pcb *); int i386_get_ldt(struct proc *, void *, register_t *); int i386_set_ldt(struct proc *, void *, register_t *); #endif diff --git a/sys/arch/i386/include/pcb.h b/sys/arch/i386/include/pcb.h index a3224657358..4a7c0094ef2 100644 --- a/sys/arch/i386/include/pcb.h +++ b/sys/arch/i386/include/pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcb.h,v 1.9 2003/06/02 23:27:47 millert Exp $ */ +/* $OpenBSD: pcb.h,v 1.10 2004/02/01 12:26:45 grange Exp $ */ /* $NetBSD: pcb.h,v 1.21 1996/01/08 13:51:42 mycroft Exp $ */ /*- @@ -70,8 +70,6 @@ struct pcb { /* * Software pcb (extension) */ - int pcb_flags; -#define PCB_USER_LDT 0x01 /* has user-set LDT */ caddr_t pcb_onfault; /* copyin/out fault recovery */ int vm86_eflags; /* virtual eflags for vm86 mode */ int vm86_flagmask; /* flag mask for vm86 mode */ |