xref: /titanic_51/usr/src/uts/intel/ia32/syscall/getcontext.c (revision f8d8d7b32d22897bd2af32f5fa5bbdc9c0797277)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/vmparam.h>
35 #include <sys/systm.h>
36 #include <sys/signal.h>
37 #include <sys/stack.h>
38 #include <sys/regset.h>
39 #include <sys/privregs.h>
40 #include <sys/frame.h>
41 #include <sys/proc.h>
42 #include <sys/psw.h>
43 #include <sys/ucontext.h>
44 #include <sys/asm_linkage.h>
45 #include <sys/errno.h>
46 #include <sys/archsystm.h>
47 #include <sys/schedctl.h>
48 #include <sys/debug.h>
49 #include <sys/sysmacros.h>
50 
51 /*
52  * Save user context.
53  */
54 void
55 savecontext(ucontext_t *ucp, k_sigset_t mask)
56 {
57 	proc_t *p = ttoproc(curthread);
58 	klwp_t *lwp = ttolwp(curthread);
59 	struct regs *rp = lwptoregs(lwp);
60 
61 	/*
62 	 * We unconditionally assign to every field through the end
63 	 * of the gregs, but we need to bzero() everything -after- that
64 	 * to avoid having any kernel stack garbage escape to userland.
65 	 */
66 	bzero(&ucp->uc_mcontext.fpregs, sizeof (ucontext_t) -
67 	    offsetof(ucontext_t, uc_mcontext.fpregs));
68 
69 	ucp->uc_flags = UC_ALL;
70 	ucp->uc_link = (struct ucontext *)lwp->lwp_oldcontext;
71 
72 	/*
73 	 * Try to copyin() the ustack if one is registered. If the stack
74 	 * has zero size, this indicates that stack bounds checking has
75 	 * been disabled for this LWP. If stack bounds checking is disabled
76 	 * or the copyin() fails, we fall back to the legacy behavior.
77 	 */
78 	if (lwp->lwp_ustack == NULL ||
79 	    copyin((void *)lwp->lwp_ustack, &ucp->uc_stack,
80 	    sizeof (ucp->uc_stack)) != 0 ||
81 	    ucp->uc_stack.ss_size == 0) {
82 
83 		if (lwp->lwp_sigaltstack.ss_flags == SS_ONSTACK) {
84 			ucp->uc_stack = lwp->lwp_sigaltstack;
85 		} else {
86 			ucp->uc_stack.ss_sp = p->p_usrstack - p->p_stksize;
87 			ucp->uc_stack.ss_size = p->p_stksize;
88 			ucp->uc_stack.ss_flags = 0;
89 		}
90 	}
91 
92 	getgregs(lwp, ucp->uc_mcontext.gregs);
93 	if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN)
94 		getfpregs(lwp, &ucp->uc_mcontext.fpregs);
95 	else
96 		ucp->uc_flags &= ~UC_FPU;
97 
98 	sigktou(&mask, &ucp->uc_sigmask);
99 	/*
100 	 * If the trace flag is set, arrange for single-stepping and
101 	 * turn off the trace flag.
102 	 */
103 	if (rp->r_ps & PS_T) {
104 		lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
105 		rp->r_ps &= ~PS_T;
106 	}
107 }
108 
109 /*
110  * Restore user context.
111  */
112 void
113 restorecontext(ucontext_t *ucp)
114 {
115 	kthread_t *t = curthread;
116 	klwp_t *lwp = ttolwp(t);
117 
118 	lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link;
119 
120 	if (ucp->uc_flags & UC_STACK) {
121 		if (ucp->uc_stack.ss_flags == SS_ONSTACK)
122 			lwp->lwp_sigaltstack = ucp->uc_stack;
123 		else
124 			lwp->lwp_sigaltstack.ss_flags &= ~SS_ONSTACK;
125 	}
126 
127 	if (ucp->uc_flags & UC_CPU) {
128 		/*
129 		 * If the trace flag is set, mark the lwp to take a
130 		 * single-step trap on return to user level (below).
131 		 * The x86 lcall interface and sysenter has already done this,
132 		 * and turned off the flag, but amd64 syscall interface has not.
133 		 */
134 		if (lwptoregs(lwp)->r_ps & PS_T)
135 			lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
136 		setgregs(lwp, ucp->uc_mcontext.gregs);
137 		lwp->lwp_eosys = JUSTRETURN;
138 		t->t_post_sys = 1;
139 	}
140 
141 	if (ucp->uc_flags & UC_FPU)
142 		setfpregs(lwp, &ucp->uc_mcontext.fpregs);
143 
144 	if (ucp->uc_flags & UC_SIGMASK) {
145 		proc_t *p = ttoproc(t);
146 
147 		mutex_enter(&p->p_lock);
148 		schedctl_finish_sigblock(t);
149 		sigutok(&ucp->uc_sigmask, &t->t_hold);
150 		if (sigcheck(p, t))
151 			t->t_sig_check = 1;
152 		mutex_exit(&p->p_lock);
153 	}
154 }
155 
156 
157 int
158 getsetcontext(int flag, void *arg)
159 {
160 	ucontext_t uc;
161 	ucontext_t *ucp;
162 	klwp_t *lwp = ttolwp(curthread);
163 	stack_t dummy_stk;
164 
165 	/*
166 	 * In future releases, when the ucontext structure grows,
167 	 * getcontext should be modified to only return the fields
168 	 * specified in the uc_flags.  That way, the structure can grow
169 	 * and still be binary compatible will all .o's which will only
170 	 * have old fields defined in uc_flags
171 	 */
172 
173 	switch (flag) {
174 	default:
175 		return (set_errno(EINVAL));
176 
177 	case GETCONTEXT:
178 		if (schedctl_sigblock(curthread)) {
179 			proc_t *p = ttoproc(curthread);
180 			mutex_enter(&p->p_lock);
181 			schedctl_finish_sigblock(curthread);
182 			mutex_exit(&p->p_lock);
183 		}
184 		savecontext(&uc, curthread->t_hold);
185 		if (copyout(&uc, arg, sizeof (uc)))
186 			return (set_errno(EFAULT));
187 		return (0);
188 
189 	case SETCONTEXT:
190 		ucp = arg;
191 		if (ucp == NULL)
192 			exit(CLD_EXITED, 0);
193 		/*
194 		 * Don't copyin filler or floating state unless we need it.
195 		 * The ucontext_t struct and fields are specified in the ABI.
196 		 */
197 		if (copyin(ucp, &uc, sizeof (ucontext_t) -
198 		    sizeof (uc.uc_filler) -
199 		    sizeof (uc.uc_mcontext.fpregs))) {
200 			return (set_errno(EFAULT));
201 		}
202 
203 		if ((uc.uc_flags & UC_FPU) &&
204 		    copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs,
205 		    sizeof (uc.uc_mcontext.fpregs))) {
206 			return (set_errno(EFAULT));
207 		}
208 
209 		restorecontext(&uc);
210 
211 		if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
212 			(void) copyout(&uc.uc_stack, (stack_t *)lwp->lwp_ustack,
213 			    sizeof (uc.uc_stack));
214 		return (0);
215 
216 	case GETUSTACK:
217 		if (copyout(&lwp->lwp_ustack, arg, sizeof (caddr_t)))
218 			return (set_errno(EFAULT));
219 		return (0);
220 
221 	case SETUSTACK:
222 		if (copyin(arg, &dummy_stk, sizeof (dummy_stk)))
223 			return (set_errno(EFAULT));
224 		lwp->lwp_ustack = (uintptr_t)arg;
225 		return (0);
226 	}
227 }
228 
229 #ifdef _SYSCALL32_IMPL
230 
231 /*
232  * Save user context for 32-bit processes.
233  */
234 void
235 savecontext32(ucontext32_t *ucp, k_sigset_t mask)
236 {
237 	proc_t *p = ttoproc(curthread);
238 	klwp_t *lwp = ttolwp(curthread);
239 	struct regs *rp = lwptoregs(lwp);
240 
241 	bzero(&ucp->uc_mcontext.fpregs, sizeof (ucontext32_t) -
242 	    offsetof(ucontext32_t, uc_mcontext.fpregs));
243 
244 	ucp->uc_flags = UC_ALL;
245 	ucp->uc_link = (caddr32_t)lwp->lwp_oldcontext;
246 
247 	if (lwp->lwp_ustack == NULL ||
248 	    copyin((void *)lwp->lwp_ustack, &ucp->uc_stack,
249 	    sizeof (ucp->uc_stack)) != 0 ||
250 	    ucp->uc_stack.ss_size == 0) {
251 
252 		if (lwp->lwp_sigaltstack.ss_flags == SS_ONSTACK) {
253 			ucp->uc_stack.ss_sp =
254 			    (caddr32_t)(uintptr_t)lwp->lwp_sigaltstack.ss_sp;
255 			ucp->uc_stack.ss_size =
256 			    (size32_t)lwp->lwp_sigaltstack.ss_size;
257 			ucp->uc_stack.ss_flags = SS_ONSTACK;
258 		} else {
259 			ucp->uc_stack.ss_sp = (caddr32_t)(uintptr_t)
260 			    (p->p_usrstack - p->p_stksize);
261 			ucp->uc_stack.ss_size = (size32_t)p->p_stksize;
262 			ucp->uc_stack.ss_flags = 0;
263 		}
264 	}
265 
266 	getgregs32(lwp, ucp->uc_mcontext.gregs);
267 	if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN)
268 		getfpregs32(lwp, &ucp->uc_mcontext.fpregs);
269 	else
270 		ucp->uc_flags &= ~UC_FPU;
271 
272 	sigktou(&mask, &ucp->uc_sigmask);
273 	/*
274 	 * If the trace flag is set, arrange for single-stepping and
275 	 * turn off the trace flag.
276 	 */
277 	if (rp->r_ps & PS_T) {
278 		lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
279 		rp->r_ps &= ~PS_T;
280 	}
281 }
282 
283 int
284 getsetcontext32(int flag, void *arg)
285 {
286 	ucontext32_t uc;
287 	ucontext_t ucnat;
288 	ucontext32_t *ucp;
289 	klwp_t *lwp = ttolwp(curthread);
290 	caddr32_t ustack32;
291 	stack32_t dummy_stk32;
292 
293 	switch (flag) {
294 	default:
295 		return (set_errno(EINVAL));
296 
297 	case GETCONTEXT:
298 		if (schedctl_sigblock(curthread)) {
299 			proc_t *p = ttoproc(curthread);
300 			mutex_enter(&p->p_lock);
301 			schedctl_finish_sigblock(curthread);
302 			mutex_exit(&p->p_lock);
303 		}
304 		savecontext32(&uc, curthread->t_hold);
305 		if (copyout(&uc, arg, sizeof (uc)))
306 			return (set_errno(EFAULT));
307 		return (0);
308 
309 	case SETCONTEXT:
310 		ucp = arg;
311 		if (ucp == NULL)
312 			exit(CLD_EXITED, 0);
313 		if (copyin(ucp, &uc, sizeof (uc) -
314 		    sizeof (uc.uc_filler) -
315 		    sizeof (uc.uc_mcontext.fpregs))) {
316 			return (set_errno(EFAULT));
317 		}
318 		if ((uc.uc_flags & UC_FPU) &&
319 		    copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs,
320 		    sizeof (uc.uc_mcontext.fpregs))) {
321 			return (set_errno(EFAULT));
322 		}
323 
324 		ucontext_32ton(&uc, &ucnat);
325 		restorecontext(&ucnat);
326 
327 		if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
328 			(void) copyout(&uc.uc_stack,
329 			    (stack32_t *)lwp->lwp_ustack, sizeof (uc.uc_stack));
330 		return (0);
331 
332 	case GETUSTACK:
333 		ustack32 = (caddr32_t)lwp->lwp_ustack;
334 		if (copyout(&ustack32, arg, sizeof (ustack32)))
335 			return (set_errno(EFAULT));
336 		return (0);
337 
338 	case SETUSTACK:
339 		if (copyin(arg, &dummy_stk32, sizeof (dummy_stk32)))
340 			return (set_errno(EFAULT));
341 		lwp->lwp_ustack = (uintptr_t)arg;
342 		return (0);
343 	}
344 }
345 
346 #endif	/* _SYSCALL32_IMPL */
347