diff options
Diffstat (limited to 'sys/arch/i386/i386/sys_machdep.c')
-rw-r--r-- | sys/arch/i386/i386/sys_machdep.c | 275 |
1 files changed, 151 insertions, 124 deletions
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 */ |