1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/disp.h> 31 #include <sys/sysmacros.h> 32 #include <sys/cpuvar.h> 33 #include <sys/systm.h> 34 #include <sys/thread.h> 35 #include <sys/lwp.h> 36 #include <sys/segments.h> 37 #include <sys/privregs.h> 38 #include <sys/cmn_err.h> 39 40 int 41 lwp_setprivate(klwp_t *lwp, int which, uintptr_t base) 42 { 43 pcb_t *pcb = &lwp->lwp_pcb; 44 struct regs *rp = lwptoregs(lwp); 45 kthread_t *t = lwptot(lwp); 46 int thisthread = t == curthread; 47 int rval; 48 49 if (thisthread) 50 kpreempt_disable(); 51 52 #if defined(__amd64) 53 54 /* 55 * 32-bit compatibility processes point to the per-cpu GDT segment 56 * descriptors that are virtualized to the lwp. That allows 32-bit 57 * programs to mess with %fs and %gs; in particular it allows 58 * things like this: 59 * 60 * movw %gs, %ax 61 * ... 62 * movw %ax, %gs 63 * 64 * to work, which is needed by emulators for legacy application 65 * environments .. 66 * 67 * 64-bit processes may also point to a per-cpu GDT segment descriptor 68 * virtualized to the lwp. However the descriptor base is forced 69 * to zero (because we can't express the full 64-bit address range 70 * in a long mode descriptor), so don't reload segment registers 71 * in a 64-bit program! 64-bit processes must have selector values 72 * of zero for %fs and %gs to use the 64-bit fs_base and gs_base 73 * respectively. 74 */ 75 if (pcb->pcb_rupdate == 0) { 76 pcb->pcb_ds = rp->r_ds; 77 pcb->pcb_es = rp->r_es; 78 pcb->pcb_fs = rp->r_fs; 79 pcb->pcb_gs = rp->r_gs; 80 pcb->pcb_rupdate = 1; 81 t->t_post_sys = 1; 82 } 83 ASSERT(t->t_post_sys); 84 85 switch (which) { 86 case _LWP_FSBASE: 87 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 88 set_usegd(&pcb->pcb_fsdesc, SDP_LONG, 0, 0, 89 SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); 90 rval = pcb->pcb_fs = 0; /* null gdt descriptor */ 91 } else { 92 set_usegd(&pcb->pcb_fsdesc, SDP_SHORT, (void *)base, -1, 93 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 94 rval = pcb->pcb_fs = LWPFS_SEL; 95 } 96 if (thisthread) 97 gdt_update_usegd(GDT_LWPFS, &pcb->pcb_fsdesc); 98 99 pcb->pcb_fsbase = base; 100 break; 101 case _LWP_GSBASE: 102 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 103 set_usegd(&pcb->pcb_gsdesc, SDP_LONG, 0, 0, 104 SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); 105 rval = pcb->pcb_gs = 0; /* null gdt descriptor */ 106 } else { 107 set_usegd(&pcb->pcb_gsdesc, SDP_SHORT, (void *)base, -1, 108 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 109 rval = pcb->pcb_gs = LWPGS_SEL; 110 } 111 if (thisthread) 112 gdt_update_usegd(GDT_LWPGS, &pcb->pcb_gsdesc); 113 114 pcb->pcb_gsbase = base; 115 break; 116 default: 117 rval = -1; 118 break; 119 } 120 121 #elif defined(__i386) 122 123 /* 124 * 32-bit processes point to the per-cpu GDT segment 125 * descriptors that are virtualized to the lwp. 126 */ 127 128 switch (which) { 129 case _LWP_FSBASE: 130 set_usegd(&pcb->pcb_fsdesc, (void *)base, -1, 131 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 132 if (thisthread) 133 gdt_update_usegd(GDT_LWPFS, &pcb->pcb_fsdesc); 134 135 rval = rp->r_fs = LWPFS_SEL; 136 break; 137 case _LWP_GSBASE: 138 set_usegd(&pcb->pcb_gsdesc, (void *)base, -1, 139 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 140 if (thisthread) 141 gdt_update_usegd(GDT_LWPGS, &pcb->pcb_gsdesc); 142 143 rval = rp->r_gs = LWPGS_SEL; 144 break; 145 default: 146 rval = -1; 147 break; 148 } 149 150 #endif /* __i386 */ 151 152 if (thisthread) 153 kpreempt_enable(); 154 return (rval); 155 } 156 157 static int 158 lwp_getprivate(klwp_t *lwp, int which, uintptr_t base) 159 { 160 pcb_t *pcb = &lwp->lwp_pcb; 161 struct regs *rp = lwptoregs(lwp); 162 uintptr_t sbase; 163 int error = 0; 164 165 ASSERT(lwptot(lwp) == curthread); 166 167 kpreempt_disable(); 168 switch (which) { 169 #if defined(__amd64) 170 171 case _LWP_FSBASE: 172 if ((sbase = pcb->pcb_fsbase) != 0) { 173 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 174 if (pcb->pcb_rupdate == 1) { 175 if (pcb->pcb_fs == 0) 176 break; 177 } else { 178 if (rp->r_fs == 0) 179 break; 180 } 181 } else { 182 if (pcb->pcb_rupdate == 1) { 183 if (pcb->pcb_fs == LWPFS_SEL) 184 break; 185 } else { 186 if (rp->r_fs == LWPFS_SEL) 187 break; 188 } 189 } 190 } 191 error = EINVAL; 192 break; 193 case _LWP_GSBASE: 194 if ((sbase = pcb->pcb_gsbase) != 0) { 195 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 196 if (pcb->pcb_rupdate == 1) { 197 if (pcb->pcb_gs == 0) 198 break; 199 } else { 200 if (rp->r_gs == 0) 201 break; 202 } 203 } else { 204 if (pcb->pcb_rupdate == 1) { 205 if (pcb->pcb_gs == LWPGS_SEL) 206 break; 207 } else { 208 if (rp->r_gs == LWPGS_SEL) 209 break; 210 } 211 } 212 } 213 error = EINVAL; 214 break; 215 216 #elif defined(__i386) 217 218 case _LWP_FSBASE: 219 if (rp->r_fs == LWPFS_SEL) { 220 sbase = USEGD_GETBASE(&pcb->pcb_fsdesc); 221 break; 222 } 223 error = EINVAL; 224 break; 225 case _LWP_GSBASE: 226 if (rp->r_gs == LWPGS_SEL) { 227 sbase = USEGD_GETBASE(&pcb->pcb_gsdesc); 228 break; 229 } 230 error = EINVAL; 231 break; 232 233 #endif /* __i386 */ 234 235 default: 236 error = ENOTSUP; 237 break; 238 } 239 kpreempt_enable(); 240 241 if (error != 0) 242 return (error); 243 244 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 245 if (sulword((void *)base, sbase) == -1) 246 error = EFAULT; 247 #if defined(_SYSCALL32_IMPL) 248 } else { 249 if (suword32((void *)base, (uint32_t)sbase) == -1) 250 error = EFAULT; 251 #endif 252 } 253 return (error); 254 } 255 256 /* 257 * libc-private syscall for managing per-lwp %gs and %fs segment base values. 258 */ 259 int 260 syslwp_private(int cmd, int which, uintptr_t base) 261 { 262 klwp_t *lwp = ttolwp(curthread); 263 int res, error; 264 265 switch (cmd) { 266 case _LWP_SETPRIVATE: 267 res = lwp_setprivate(lwp, which, base); 268 return (res < 0 ? set_errno(ENOTSUP) : res); 269 case _LWP_GETPRIVATE: 270 error = lwp_getprivate(lwp, which, base); 271 return (error != 0 ? set_errno(error) : error); 272 default: 273 return (set_errno(ENOTSUP)); 274 } 275 } 276