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 * Copyright (c) 2018, Joyent, Inc. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/types.h> 29 #include <sys/disp.h> 30 #include <sys/sysmacros.h> 31 #include <sys/cpuvar.h> 32 #include <sys/systm.h> 33 #include <sys/thread.h> 34 #include <sys/lwp.h> 35 #include <sys/segments.h> 36 #include <sys/privregs.h> 37 #include <sys/cmn_err.h> 38 39 int 40 lwp_setprivate(klwp_t *lwp, int which, uintptr_t base) 41 { 42 pcb_t *pcb = &lwp->lwp_pcb; 43 struct regs *rp = lwptoregs(lwp); 44 kthread_t *t = lwptot(lwp); 45 int thisthread = t == curthread; 46 int rval; 47 48 if (thisthread) 49 kpreempt_disable(); 50 51 52 /* 53 * 32-bit compatibility processes point to the per-cpu GDT segment 54 * descriptors that are virtualized to the lwp. That allows 32-bit 55 * programs to mess with %fs and %gs; in particular it allows 56 * things like this: 57 * 58 * movw %gs, %ax 59 * ... 60 * movw %ax, %gs 61 * 62 * to work, which is needed by emulators for legacy application 63 * environments .. 64 * 65 * 64-bit processes may also point to a per-cpu GDT segment descriptor 66 * virtualized to the lwp. However the descriptor base is forced 67 * to zero (because we can't express the full 64-bit address range 68 * in a long mode descriptor), so don't reload segment registers 69 * in a 64-bit program! 64-bit processes must have selector values 70 * of zero for %fs and %gs to use the 64-bit fs_base and gs_base 71 * respectively. 72 */ 73 if (!PCB_NEED_UPDATE_SEGS(pcb)) { 74 pcb->pcb_ds = rp->r_ds; 75 pcb->pcb_es = rp->r_es; 76 pcb->pcb_fs = rp->r_fs; 77 pcb->pcb_gs = rp->r_gs; 78 PCB_SET_UPDATE_SEGS(pcb); 79 t->t_post_sys = 1; 80 } 81 ASSERT(t->t_post_sys); 82 83 switch (which) { 84 case _LWP_FSBASE: 85 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 86 set_usegd(&pcb->pcb_fsdesc, SDP_LONG, 0, 0, 87 SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); 88 rval = pcb->pcb_fs = 0; /* null gdt descriptor */ 89 } else { 90 set_usegd(&pcb->pcb_fsdesc, SDP_SHORT, (void *)base, -1, 91 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 92 rval = pcb->pcb_fs = LWPFS_SEL; 93 } 94 if (thisthread) 95 gdt_update_usegd(GDT_LWPFS, &pcb->pcb_fsdesc); 96 97 pcb->pcb_fsbase = base; 98 break; 99 case _LWP_GSBASE: 100 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 101 set_usegd(&pcb->pcb_gsdesc, SDP_LONG, 0, 0, 102 SDT_MEMRWA, SEL_UPL, SDP_BYTES, SDP_OP32); 103 rval = pcb->pcb_gs = 0; /* null gdt descriptor */ 104 } else { 105 set_usegd(&pcb->pcb_gsdesc, SDP_SHORT, (void *)base, -1, 106 SDT_MEMRWA, SEL_UPL, SDP_PAGES, SDP_OP32); 107 rval = pcb->pcb_gs = LWPGS_SEL; 108 } 109 if (thisthread) 110 gdt_update_usegd(GDT_LWPGS, &pcb->pcb_gsdesc); 111 112 pcb->pcb_gsbase = base; 113 break; 114 default: 115 rval = -1; 116 break; 117 } 118 119 if (thisthread) 120 kpreempt_enable(); 121 return (rval); 122 } 123 124 static int 125 lwp_getprivate(klwp_t *lwp, int which, uintptr_t base) 126 { 127 pcb_t *pcb = &lwp->lwp_pcb; 128 struct regs *rp = lwptoregs(lwp); 129 uintptr_t sbase; 130 int error = 0; 131 132 ASSERT(lwptot(lwp) == curthread); 133 134 kpreempt_disable(); 135 switch (which) { 136 case _LWP_FSBASE: 137 if ((sbase = pcb->pcb_fsbase) != 0) { 138 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 139 if (PCB_NEED_UPDATE_SEGS(pcb)) { 140 if (pcb->pcb_fs == 0) 141 break; 142 } else { 143 if (rp->r_fs == 0) 144 break; 145 } 146 } else { 147 if (PCB_NEED_UPDATE_SEGS(pcb)) { 148 if (pcb->pcb_fs == LWPFS_SEL) 149 break; 150 } else { 151 if (rp->r_fs == LWPFS_SEL) 152 break; 153 } 154 } 155 } 156 error = EINVAL; 157 break; 158 case _LWP_GSBASE: 159 if ((sbase = pcb->pcb_gsbase) != 0) { 160 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 161 if (PCB_NEED_UPDATE_SEGS(pcb)) { 162 if (pcb->pcb_gs == 0) 163 break; 164 } else { 165 if (rp->r_gs == 0) 166 break; 167 } 168 } else { 169 if (PCB_NEED_UPDATE_SEGS(pcb)) { 170 if (pcb->pcb_gs == LWPGS_SEL) 171 break; 172 } else { 173 if (rp->r_gs == LWPGS_SEL) 174 break; 175 } 176 } 177 } 178 error = EINVAL; 179 break; 180 181 182 default: 183 error = ENOTSUP; 184 break; 185 } 186 kpreempt_enable(); 187 188 if (error != 0) 189 return (error); 190 191 if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) { 192 if (sulword((void *)base, sbase) == -1) 193 error = EFAULT; 194 #if defined(_SYSCALL32_IMPL) 195 } else { 196 if (suword32((void *)base, (uint32_t)sbase) == -1) 197 error = EFAULT; 198 #endif 199 } 200 return (error); 201 } 202 203 /* 204 * libc-private syscall for managing per-lwp %gs and %fs segment base values. 205 */ 206 int 207 syslwp_private(int cmd, int which, uintptr_t base) 208 { 209 klwp_t *lwp = ttolwp(curthread); 210 int res, error; 211 212 switch (cmd) { 213 case _LWP_SETPRIVATE: 214 res = lwp_setprivate(lwp, which, base); 215 return (res < 0 ? set_errno(ENOTSUP) : res); 216 case _LWP_GETPRIVATE: 217 error = lwp_getprivate(lwp, which, base); 218 return (error != 0 ? set_errno(error) : error); 219 default: 220 return (set_errno(ENOTSUP)); 221 } 222 } 223