diff options
author | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-03-12 03:52:27 +0000 |
---|---|---|
committer | Philip Guenthe <guenther@cvs.openbsd.org> | 2011-03-12 03:52:27 +0000 |
commit | c62a833c3eed0faa13ce022b6d4eee3119bffd85 (patch) | |
tree | cf68d2b3fbd1afd58c33da5d40eee730557be64e /sys | |
parent | cd06312d1f6bdbfe322dd8bf9ada43332f0d48e3 (diff) |
Provide distinct segments for the %fs and %gs selectors to use by
default, with per-rthread base offsets and with sysarch() functions,
I386_{GET,SET}_{FS,GS}BASE, for fetching and setting those base
offsets. This is necessary for both rthread and Linux compat support.
suggestions from kettenis@, prodding from pirofti@ and deraadt@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/i386/i386/locore.s | 5 | ||||
-rw-r--r-- | sys/arch/i386/i386/machdep.c | 26 | ||||
-rw-r--r-- | sys/arch/i386/i386/pmap.c | 8 | ||||
-rw-r--r-- | sys/arch/i386/i386/sys_machdep.c | 55 | ||||
-rw-r--r-- | sys/arch/i386/include/pcb.h | 12 | ||||
-rw-r--r-- | sys/arch/i386/include/segments.h | 9 | ||||
-rw-r--r-- | sys/arch/i386/include/sysarch.h | 10 |
7 files changed, 103 insertions, 22 deletions
diff --git a/sys/arch/i386/i386/locore.s b/sys/arch/i386/i386/locore.s index 3eedfb20b5b..1f90906ffa6 100644 --- a/sys/arch/i386/i386/locore.s +++ b/sys/arch/i386/i386/locore.s @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.s,v 1.130 2010/07/03 04:54:32 kettenis Exp $ */ +/* $OpenBSD: locore.s,v 1.131 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: locore.s,v 1.145 1996/05/03 19:41:19 christos Exp $ */ /*- @@ -112,6 +112,7 @@ movl $GSEL(GDATA_SEL, SEL_KPL),%eax ; \ movw %ax,%ds ; \ movw %ax,%es ; \ + xorl %eax,%eax ; /* $GSEL(GNULL_SEL, SEL_KPL) == 0 */ \ movw %ax,%gs ; \ pushl %fs ; \ movl $GSEL(GCPU_SEL, SEL_KPL),%eax ; \ @@ -1495,7 +1496,7 @@ NENTRY(resume_pop_ds) movw %ax,%es NENTRY(resume_pop_es) pushl %gs - movl $GSEL(GDATA_SEL, SEL_KPL),%eax + xorl %eax,%eax /* $GSEL(GNULL_SEL, SEL_KPL) == 0 */ movw %ax,%gs NENTRY(resume_pop_gs) pushl %fs diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index ae0e8dc243a..b6af6b52eef 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.487 2011/01/27 21:27:44 jsg Exp $ */ +/* $OpenBSD: machdep.c,v 1.488 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */ /*- @@ -2282,8 +2282,8 @@ sendsig(sig_t catcher, int sig, int mask, u_long code, int type, /* * Build context to run handler in. */ - tf->tf_fs = GSEL(GUDATA_SEL, SEL_UPL); - tf->tf_gs = GSEL(GUDATA_SEL, SEL_UPL); + tf->tf_fs = GSEL(GUFS_SEL, SEL_UPL); + tf->tf_gs = GSEL(GUGS_SEL, SEL_UPL); tf->tf_es = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_ds = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_eip = p->p_sigcode; @@ -2708,25 +2708,33 @@ setregs(struct proc *p, struct exec_package *pack, u_long stack, /* * Reset the code segment limit to I386_MAX_EXE_ADDR in the pmap; - * this gets copied into the GDT and LDT for {G,L}UCODE_SEL by - * pmap_activate(). + * this gets copied into the GDT for GUCODE_SEL by pmap_activate(). + * Similarly, reset the base of each of the two thread data + * segments to zero in the pcb; they'll get copied into the + * GDT for GUFS_SEL and GUGS_SEL. */ setsegment(&pmap->pm_codeseg, 0, atop(I386_MAX_EXE_ADDR) - 1, SDT_MEMERA, SEL_UPL, 1, 1); + setsegment(&pcb->pcb_threadsegs[TSEG_FS], 0, + atop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMRWA, SEL_UPL, 1, 1); + setsegment(&pcb->pcb_threadsegs[TSEG_GS], 0, + atop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMRWA, SEL_UPL, 1, 1); /* * And update the GDT since we return to the user process * by leaving the syscall (we don't do another pmap_activate()). */ curcpu()->ci_gdt[GUCODE_SEL].sd = pmap->pm_codeseg; + curcpu()->ci_gdt[GUFS_SEL].sd = pcb->pcb_threadsegs[TSEG_FS]; + curcpu()->ci_gdt[GUGS_SEL].sd = pcb->pcb_threadsegs[TSEG_GS]; /* * And reset the hiexec marker in the pmap. */ pmap->pm_hiexec = 0; - tf->tf_fs = GSEL(GUDATA_SEL, SEL_UPL); - tf->tf_gs = GSEL(GUDATA_SEL, SEL_UPL); + tf->tf_fs = GSEL(GUFS_SEL, SEL_UPL); + tf->tf_gs = GSEL(GUGS_SEL, SEL_UPL); tf->tf_es = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_ds = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_edi = 0; @@ -2928,6 +2936,10 @@ init386(paddr_t first_avail) SDT_MEMRWA, SEL_UPL, 1, 1); setsegment(&gdt[GCPU_SEL].sd, &cpu_info_primary, sizeof(struct cpu_info)-1, SDT_MEMRWA, SEL_KPL, 0, 0); + setsegment(&gdt[GUFS_SEL].sd, 0, atop(VM_MAXUSER_ADDRESS) - 1, + SDT_MEMRWA, SEL_UPL, 1, 1); + setsegment(&gdt[GUGS_SEL].sd, 0, atop(VM_MAXUSER_ADDRESS) - 1, + SDT_MEMRWA, SEL_UPL, 1, 1); /* exceptions */ setgate(&idt[ 0], &IDTVEC(div), 0, SDT_SYS386TGT, SEL_KPL, GCODE_SEL); diff --git a/sys/arch/i386/i386/pmap.c b/sys/arch/i386/i386/pmap.c index 291c503799f..a27ee0c3f1a 100644 --- a/sys/arch/i386/i386/pmap.c +++ b/sys/arch/i386/i386/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.151 2010/11/30 19:28:59 kettenis Exp $ */ +/* $OpenBSD: pmap.c,v 1.152 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: pmap.c,v 1.91 2000/06/02 17:46:37 thorpej Exp $ */ /* @@ -1692,6 +1692,8 @@ pmap_switch(struct proc *o, struct proc *p) * correct code segment X limit) in the GDT. */ self->ci_gdt[GUCODE_SEL].sd = pmap->pm_codeseg; + self->ci_gdt[GUFS_SEL].sd = pcb->pcb_threadsegs[TSEG_FS]; + self->ci_gdt[GUGS_SEL].sd = pcb->pcb_threadsegs[TSEG_GS]; lldt(pcb->pcb_ldt_sel); } @@ -2297,7 +2299,7 @@ pmap_write_protect(struct pmap *pmap, vaddr_t sva, vaddr_t eva, md_prot |= PG_u; else if (va < VM_MAX_ADDRESS) /* XXX: write-prot our PTES? never! */ - md_prot |= (PG_u | PG_RW); + md_prot |= PG_RW; spte = &ptes[atop(va)]; epte = &ptes[atop(blockend)]; @@ -2576,7 +2578,7 @@ enter_now: if (va < VM_MAXUSER_ADDRESS) npte |= PG_u; else if (va < VM_MAX_ADDRESS) - npte |= (PG_u | PG_RW); /* XXXCDC: no longer needed? */ + npte |= PG_RW; /* XXXCDC: no longer needed? */ if (pmap == pmap_kernel()) npte |= pmap_pg_g; if (flags & VM_PROT_READ) diff --git a/sys/arch/i386/i386/sys_machdep.c b/sys/arch/i386/i386/sys_machdep.c index 16a28e3634d..f220dbafdff 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.27 2010/11/20 20:21:13 miod Exp $ */ +/* $OpenBSD: sys_machdep.c,v 1.28 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: sys_machdep.c,v 1.28 1996/05/03 19:42:29 christos Exp $ */ /*- @@ -72,6 +72,8 @@ extern struct vm_map *kernel_map; 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 *); +int i386_get_threadbase(struct proc *, void *, int); +int i386_set_threadbase(struct proc *, void *, int); #ifdef USER_LDT @@ -395,6 +397,41 @@ i386_set_ioperm(struct proc *p, void *args, register_t *retval) } int +i386_get_threadbase(struct proc *p, void *args, int which) +{ + struct segment_descriptor *sdp = + &p->p_addr->u_pcb.pcb_threadsegs[which]; + uint32_t base = sdp->sd_hibase << 24 | sdp->sd_lobase; + + return copyout(&base, args, sizeof(base)); +} + +int +i386_set_threadbase(struct proc *p, void *args, int which) +{ + int error; + uint32_t base; + struct segment_descriptor *sdp; + + if ((error = copyin(args, &base, sizeof(base))) != 0) + return error; + + /* + * We can't place a limit on the segment used by the library + * thread register (%gs) because the ELF ABI for i386 places + * data structures both before and after base pointer, using + * negative offsets for some bits (the static (load-time) + * TLS slots) and non-negative for others (the TCB block, + * including the pointer to the TLS dynamic thread vector). + * Protection must be provided by the paging subsystem. + */ + sdp = &p->p_addr->u_pcb.pcb_threadsegs[which]; + setsegment(sdp, (void *)base, 0xfffff, SDT_MEMRWA, SEL_UPL, 1, 1); + curcpu()->ci_gdt[which == TSEG_FS ? GUFS_SEL : GUGS_SEL].sd = *sdp; + return 0; +} + +int sys_sysarch(struct proc *p, void *v, register_t *retval) { struct sys_sysarch_args /* { @@ -432,6 +469,22 @@ sys_sysarch(struct proc *p, void *v, register_t *retval) break; #endif + case I386_GET_FSBASE: + error = i386_get_threadbase(p, SCARG(uap, parms), TSEG_FS); + break; + + case I386_SET_FSBASE: + error = i386_set_threadbase(p, SCARG(uap, parms), TSEG_FS); + break; + + case I386_GET_GSBASE: + error = i386_get_threadbase(p, SCARG(uap, parms), TSEG_GS); + break; + + case I386_SET_GSBASE: + error = i386_set_threadbase(p, SCARG(uap, parms), TSEG_GS); + break; + default: error = EINVAL; break; diff --git a/sys/arch/i386/include/pcb.h b/sys/arch/i386/include/pcb.h index fa3623eafad..66c1b816a63 100644 --- a/sys/arch/i386/include/pcb.h +++ b/sys/arch/i386/include/pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcb.h,v 1.14 2007/10/03 07:51:26 kettenis Exp $ */ +/* $OpenBSD: pcb.h,v 1.15 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: pcb.h,v 1.21 1996/01/08 13:51:42 mycroft Exp $ */ /*- @@ -59,13 +59,13 @@ struct pcb { #define pcb_ebp pcb_tss.tss_ebp #define pcb_cs pcb_tss.tss_cs #define pcb_ldt_sel pcb_tss.tss_ldt - int pcb_tss_sel; union descriptor *pcb_ldt; /* per process (user) LDT */ int pcb_ldt_len; /* number of LDT entries */ - int pcb_cr0; /* saved image of CR0 */ - int pcb_pad[2]; /* savefpu on 16-byte boundary */ union savefpu pcb_savefpu; /* floating point state for FPU */ + int pcb_cr0; /* saved image of CR0 */ struct emcsts pcb_saveemc; /* Cyrix EMC state */ + struct segment_descriptor pcb_threadsegs[2]; + /* per-thread descriptors */ /* * Software pcb (extension) */ @@ -81,6 +81,10 @@ struct pcb { #define PCB_SAVECTX 0x00000001 }; +/* the indexes of the %fs/%gs segments in pcb_threadsegs */ +#define TSEG_FS 0 +#define TSEG_GS 1 + /* * The pcb is augmented with machine-dependent additional data for * core dumps. For the i386, there is nothing to add. diff --git a/sys/arch/i386/include/segments.h b/sys/arch/i386/include/segments.h index a6932a4e0fd..1a56a1887e2 100644 --- a/sys/arch/i386/include/segments.h +++ b/sys/arch/i386/include/segments.h @@ -1,4 +1,4 @@ -/* $OpenBSD: segments.h,v 1.18 2010/12/24 20:26:30 tedu Exp $ */ +/* $OpenBSD: segments.h,v 1.19 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: segments.h,v 1.23 1996/02/01 22:31:03 mycroft Exp $ */ /*- @@ -215,15 +215,16 @@ void idt_vec_free(int); #define GCODE_SEL 1 /* Kernel code descriptor */ #define GDATA_SEL 2 /* Kernel data descriptor */ #define GLDT_SEL 3 /* Default LDT descriptor */ -#define GUCODE1_SEL 4 /* User code descriptor */ +#define GCPU_SEL 4 /* per-CPU segment */ #define GUCODE_SEL 5 /* User code descriptor (a stack short) */ #define GUDATA_SEL 6 /* User data descriptor */ #define GAPM32CODE_SEL 7 /* 32 bit APM code descriptor */ #define GAPM16CODE_SEL 8 /* 16 bit APM code descriptor */ #define GAPMDATA_SEL 9 /* APM data descriptor */ #define GICODE_SEL 10 /* Interrupt code descriptor (same as Kernel code) */ -#define GCPU_SEL 11 /* per-CPU segment */ -#define NGDT 12 +#define GUFS_SEL 11 /* User per-thread (%fs) descriptor */ +#define GUGS_SEL 12 /* User per-thread (%gs) descriptor */ +#define NGDT 13 /* * Entries in the Local Descriptor Table (LDT) diff --git a/sys/arch/i386/include/sysarch.h b/sys/arch/i386/include/sysarch.h index cae749a2b53..1115026c63d 100644 --- a/sys/arch/i386/include/sysarch.h +++ b/sys/arch/i386/include/sysarch.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sysarch.h,v 1.5 2004/09/15 18:46:50 deraadt Exp $ */ +/* $OpenBSD: sysarch.h,v 1.6 2011/03/12 03:52:26 guenther Exp $ */ /* $NetBSD: sysarch.h,v 1.8 1996/01/08 13:51:44 mycroft Exp $ */ #ifndef _I386_SYSARCH_H_ @@ -13,6 +13,10 @@ #define I386_GET_IOPERM 3 #define I386_SET_IOPERM 4 #define I386_VM86 5 +#define I386_GET_FSBASE 6 +#define I386_SET_FSBASE 7 +#define I386_GET_GSBASE 8 +#define I386_SET_GSBASE 9 struct i386_get_ldt_args { int start; @@ -44,6 +48,10 @@ int i386_set_ldt(int, union descriptor *, int); int i386_iopl(int); int i386_get_ioperm(u_long *); int i386_set_ioperm(u_long *); +int i386_get_fsbase(void **); +int i386_set_fsbase(void *); +int i386_get_gsbase(void **); +int i386_set_gsbase(void *); int sysarch(int, void *); #endif |