xref: /titanic_44/usr/src/uts/intel/ia32/syscall/getcontext.c (revision dc20a3024900c47dd2ee44b9707e6df38f7d62a5)
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 2007 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 	/*
93 	 * If either the trace flag or REQUEST_STEP is set,
94 	 * arrange for single-stepping and turn off the trace flag.
95 	 */
96 	if ((rp->r_ps & PS_T) || (lwp->lwp_pcb.pcb_flags & REQUEST_STEP)) {
97 		/*
98 		 * Clear PS_T so that saved user context won't have trace
99 		 * flag set.
100 		 */
101 		rp->r_ps &= ~PS_T;
102 
103 		if (!(lwp->lwp_pcb.pcb_flags & REQUEST_NOSTEP)) {
104 			lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
105 			/*
106 			 * trap() always checks DEBUG_PENDING before
107 			 * checking for any pending signal. This at times
108 			 * can potentially lead to DEBUG_PENDING not being
109 			 * honoured. (for eg: the lwp is stopped by
110 			 * stop_on_fault() called from trap(), after being
111 			 * awakened it might see a pending signal and call
112 			 * savecontext(), however on the way back to userland
113 			 * there is no place it can be detected). Hence in
114 			 * anticipation of such occassions, set AST flag for
115 			 * the thread which will make the thread take an
116 			 * excursion through trap() where it will be handled
117 			 * appropriately.
118 			 */
119 			aston(curthread);
120 		}
121 	}
122 
123 	getgregs(lwp, ucp->uc_mcontext.gregs);
124 	if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN)
125 		getfpregs(lwp, &ucp->uc_mcontext.fpregs);
126 	else
127 		ucp->uc_flags &= ~UC_FPU;
128 
129 	sigktou(&mask, &ucp->uc_sigmask);
130 }
131 
132 /*
133  * Restore user context.
134  */
135 void
136 restorecontext(ucontext_t *ucp)
137 {
138 	kthread_t *t = curthread;
139 	klwp_t *lwp = ttolwp(t);
140 
141 	lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link;
142 
143 	if (ucp->uc_flags & UC_STACK) {
144 		if (ucp->uc_stack.ss_flags == SS_ONSTACK)
145 			lwp->lwp_sigaltstack = ucp->uc_stack;
146 		else
147 			lwp->lwp_sigaltstack.ss_flags &= ~SS_ONSTACK;
148 	}
149 
150 	if (ucp->uc_flags & UC_CPU) {
151 		/*
152 		 * If the trace flag is set, mark the lwp to take a
153 		 * single-step trap on return to user level (below).
154 		 * The x86 lcall interface and sysenter has already done this,
155 		 * and turned off the flag, but amd64 syscall interface has not.
156 		 */
157 		if (lwptoregs(lwp)->r_ps & PS_T)
158 			lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
159 		setgregs(lwp, ucp->uc_mcontext.gregs);
160 		lwp->lwp_eosys = JUSTRETURN;
161 		t->t_post_sys = 1;
162 		aston(curthread);
163 	}
164 
165 	if (ucp->uc_flags & UC_FPU)
166 		setfpregs(lwp, &ucp->uc_mcontext.fpregs);
167 
168 	if (ucp->uc_flags & UC_SIGMASK) {
169 		proc_t *p = ttoproc(t);
170 
171 		mutex_enter(&p->p_lock);
172 		schedctl_finish_sigblock(t);
173 		sigutok(&ucp->uc_sigmask, &t->t_hold);
174 		if (sigcheck(p, t))
175 			t->t_sig_check = 1;
176 		mutex_exit(&p->p_lock);
177 	}
178 }
179 
180 
181 int
182 getsetcontext(int flag, void *arg)
183 {
184 	ucontext_t uc;
185 	ucontext_t *ucp;
186 	klwp_t *lwp = ttolwp(curthread);
187 	stack_t dummy_stk;
188 
189 	/*
190 	 * In future releases, when the ucontext structure grows,
191 	 * getcontext should be modified to only return the fields
192 	 * specified in the uc_flags.  That way, the structure can grow
193 	 * and still be binary compatible will all .o's which will only
194 	 * have old fields defined in uc_flags
195 	 */
196 
197 	switch (flag) {
198 	default:
199 		return (set_errno(EINVAL));
200 
201 	case GETCONTEXT:
202 		if (schedctl_sigblock(curthread)) {
203 			proc_t *p = ttoproc(curthread);
204 			mutex_enter(&p->p_lock);
205 			schedctl_finish_sigblock(curthread);
206 			mutex_exit(&p->p_lock);
207 		}
208 		savecontext(&uc, curthread->t_hold);
209 		if (copyout(&uc, arg, sizeof (uc)))
210 			return (set_errno(EFAULT));
211 		return (0);
212 
213 	case SETCONTEXT:
214 		ucp = arg;
215 		if (ucp == NULL)
216 			exit(CLD_EXITED, 0);
217 		/*
218 		 * Don't copyin filler or floating state unless we need it.
219 		 * The ucontext_t struct and fields are specified in the ABI.
220 		 */
221 		if (copyin(ucp, &uc, sizeof (ucontext_t) -
222 		    sizeof (uc.uc_filler) -
223 		    sizeof (uc.uc_mcontext.fpregs))) {
224 			return (set_errno(EFAULT));
225 		}
226 
227 		if ((uc.uc_flags & UC_FPU) &&
228 		    copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs,
229 		    sizeof (uc.uc_mcontext.fpregs))) {
230 			return (set_errno(EFAULT));
231 		}
232 
233 		restorecontext(&uc);
234 
235 		if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
236 			(void) copyout(&uc.uc_stack, (stack_t *)lwp->lwp_ustack,
237 			    sizeof (uc.uc_stack));
238 		return (0);
239 
240 	case GETUSTACK:
241 		if (copyout(&lwp->lwp_ustack, arg, sizeof (caddr_t)))
242 			return (set_errno(EFAULT));
243 		return (0);
244 
245 	case SETUSTACK:
246 		if (copyin(arg, &dummy_stk, sizeof (dummy_stk)))
247 			return (set_errno(EFAULT));
248 		lwp->lwp_ustack = (uintptr_t)arg;
249 		return (0);
250 	}
251 }
252 
253 #ifdef _SYSCALL32_IMPL
254 
255 /*
256  * Save user context for 32-bit processes.
257  */
258 void
259 savecontext32(ucontext32_t *ucp, k_sigset_t mask)
260 {
261 	proc_t *p = ttoproc(curthread);
262 	klwp_t *lwp = ttolwp(curthread);
263 	struct regs *rp = lwptoregs(lwp);
264 
265 	bzero(&ucp->uc_mcontext.fpregs, sizeof (ucontext32_t) -
266 	    offsetof(ucontext32_t, uc_mcontext.fpregs));
267 
268 	ucp->uc_flags = UC_ALL;
269 	ucp->uc_link = (caddr32_t)lwp->lwp_oldcontext;
270 
271 	if (lwp->lwp_ustack == NULL ||
272 	    copyin((void *)lwp->lwp_ustack, &ucp->uc_stack,
273 	    sizeof (ucp->uc_stack)) != 0 ||
274 	    ucp->uc_stack.ss_size == 0) {
275 
276 		if (lwp->lwp_sigaltstack.ss_flags == SS_ONSTACK) {
277 			ucp->uc_stack.ss_sp =
278 			    (caddr32_t)(uintptr_t)lwp->lwp_sigaltstack.ss_sp;
279 			ucp->uc_stack.ss_size =
280 			    (size32_t)lwp->lwp_sigaltstack.ss_size;
281 			ucp->uc_stack.ss_flags = SS_ONSTACK;
282 		} else {
283 			ucp->uc_stack.ss_sp = (caddr32_t)(uintptr_t)
284 			    (p->p_usrstack - p->p_stksize);
285 			ucp->uc_stack.ss_size = (size32_t)p->p_stksize;
286 			ucp->uc_stack.ss_flags = 0;
287 		}
288 	}
289 
290 	/*
291 	 * If either the trace flag or REQUEST_STEP is set, arrange
292 	 * for single-stepping and turn off the trace flag.
293 	 */
294 	if ((rp->r_ps & PS_T) || (lwp->lwp_pcb.pcb_flags & REQUEST_STEP)) {
295 		/*
296 		 * Clear PS_T so that saved user context won't have trace
297 		 * flag set.
298 		 */
299 		rp->r_ps &= ~PS_T;
300 
301 		if (!(lwp->lwp_pcb.pcb_flags & REQUEST_NOSTEP)) {
302 			lwp->lwp_pcb.pcb_flags |= DEBUG_PENDING;
303 			/*
304 			 * See comments in savecontext().
305 			 */
306 			aston(curthread);
307 		}
308 	}
309 
310 	getgregs32(lwp, ucp->uc_mcontext.gregs);
311 	if (lwp->lwp_pcb.pcb_fpu.fpu_flags & FPU_EN)
312 		getfpregs32(lwp, &ucp->uc_mcontext.fpregs);
313 	else
314 		ucp->uc_flags &= ~UC_FPU;
315 
316 	sigktou(&mask, &ucp->uc_sigmask);
317 }
318 
319 int
320 getsetcontext32(int flag, void *arg)
321 {
322 	ucontext32_t uc;
323 	ucontext_t ucnat;
324 	ucontext32_t *ucp;
325 	klwp_t *lwp = ttolwp(curthread);
326 	caddr32_t ustack32;
327 	stack32_t dummy_stk32;
328 
329 	switch (flag) {
330 	default:
331 		return (set_errno(EINVAL));
332 
333 	case GETCONTEXT:
334 		if (schedctl_sigblock(curthread)) {
335 			proc_t *p = ttoproc(curthread);
336 			mutex_enter(&p->p_lock);
337 			schedctl_finish_sigblock(curthread);
338 			mutex_exit(&p->p_lock);
339 		}
340 		savecontext32(&uc, curthread->t_hold);
341 		if (copyout(&uc, arg, sizeof (uc)))
342 			return (set_errno(EFAULT));
343 		return (0);
344 
345 	case SETCONTEXT:
346 		ucp = arg;
347 		if (ucp == NULL)
348 			exit(CLD_EXITED, 0);
349 		if (copyin(ucp, &uc, sizeof (uc) -
350 		    sizeof (uc.uc_filler) -
351 		    sizeof (uc.uc_mcontext.fpregs))) {
352 			return (set_errno(EFAULT));
353 		}
354 		if ((uc.uc_flags & UC_FPU) &&
355 		    copyin(&ucp->uc_mcontext.fpregs, &uc.uc_mcontext.fpregs,
356 		    sizeof (uc.uc_mcontext.fpregs))) {
357 			return (set_errno(EFAULT));
358 		}
359 
360 		ucontext_32ton(&uc, &ucnat);
361 		restorecontext(&ucnat);
362 
363 		if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
364 			(void) copyout(&uc.uc_stack,
365 			    (stack32_t *)lwp->lwp_ustack, sizeof (uc.uc_stack));
366 		return (0);
367 
368 	case GETUSTACK:
369 		ustack32 = (caddr32_t)lwp->lwp_ustack;
370 		if (copyout(&ustack32, arg, sizeof (ustack32)))
371 			return (set_errno(EFAULT));
372 		return (0);
373 
374 	case SETUSTACK:
375 		if (copyin(arg, &dummy_stk32, sizeof (dummy_stk32)))
376 			return (set_errno(EFAULT));
377 		lwp->lwp_ustack = (uintptr_t)arg;
378 		return (0);
379 	}
380 }
381 
382 #endif	/* _SYSCALL32_IMPL */
383