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
lwp_setprivate(klwp_t * lwp,int which,uintptr_t base)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
lwp_getprivate(klwp_t * lwp,int which,uintptr_t base)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
syslwp_private(int cmd,int which,uintptr_t base)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