xref: /illumos-gate/usr/src/lib/libc/i386/threads/machdep.c (revision c160bf3613805cfb4a89a0433ae896d3594f551f)
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 /*
23  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include "thr_uberdata.h"
27 #include <procfs.h>
28 #include <ucontext.h>
29 #include <setjmp.h>
30 
31 /*
32  * The stack needs to be 16-byte aligned with a 4-byte bias.  See comment in
33  * lib/libc/i386/gen/makectxt.c.
34  *
35  * Note: If you change it, you need to change it in the following files as
36  * well:
37  *
38  *  - lib/libc/i386/gen/makectxt.c
39  *  - lib/common/i386/crti.s
40  *  - lib/common/i386/crt1.s
41  */
42 #undef	STACK_ALIGN
43 #define	STACK_ALIGN	16
44 
45 extern int getlwpstatus(thread_t, lwpstatus_t *);
46 extern int putlwpregs(thread_t, prgregset_t);
47 
48 void *
49 setup_top_frame(void *stk, size_t stksize, ulwp_t *ulwp)
50 {
51 	uint32_t *stack;
52 	struct {
53 		uint32_t	rpc;
54 		uint32_t	arg;
55 		uint32_t	pad;
56 		uint32_t	fp;
57 		uint32_t	pc;
58 	} frame;
59 
60 	/*
61 	 * Top-of-stack must be rounded down to STACK_ALIGN and
62 	 * there must be a minimum frame.  Note: 'frame' is not a true
63 	 * stack frame (see <sys/frame.h>) but a construction made here to
64 	 * make it look like _lwp_start called the thread start function
65 	 * with a 16-byte aligned stack pointer (the address of frame.arg
66 	 * is the address that muet be aligned on a 16-byte boundary).
67 	 */
68 	stack = (uint32_t *)(((uintptr_t)stk + stksize) & ~(STACK_ALIGN-1));
69 
70 	/*
71 	 * This will return NULL if the kernel cannot allocate
72 	 * a page for the top page of the stack.  This will cause
73 	 * thr_create(), pthread_create() or pthread_attr_setstack()
74 	 * to fail, passing the problem up to the application.
75 	 */
76 	stack -= 5;	/* make the address of frame.arg be 16-byte aligned */
77 	frame.pc = 0;
78 	frame.fp = 0;	/* initial address for %ebp (see EBP below) */
79 	frame.pad = 0;
80 	frame.arg = (uint32_t)ulwp;
81 	frame.rpc = (uint32_t)_lwp_start;
82 	if (uucopy(&frame, (void *)stack, sizeof (frame)) == 0)
83 		return (stack);
84 	return (NULL);
85 }
86 
87 int
88 setup_context(ucontext_t *ucp, void *(*func)(ulwp_t *),
89 	ulwp_t *ulwp, caddr_t stk, size_t stksize)
90 {
91 	static int initialized;
92 	static greg_t fs, es, ds, cs, ss;
93 
94 	uint32_t *stack;
95 
96 	if (!initialized) {
97 		ucontext_t uc;
98 
99 		/* do this once to load the segment registers */
100 		uc.uc_flags = UC_CPU;
101 		(void) __getcontext(&uc);
102 		fs = uc.uc_mcontext.gregs[FS];
103 		es = uc.uc_mcontext.gregs[ES];
104 		ds = uc.uc_mcontext.gregs[DS];
105 		cs = uc.uc_mcontext.gregs[CS];
106 		ss = uc.uc_mcontext.gregs[SS];
107 		initialized = 1;
108 	}
109 	/* clear the context and set the segment registers */
110 	(void) memset(ucp, 0, sizeof (*ucp));
111 	ucp->uc_mcontext.gregs[FS] = fs;
112 	ucp->uc_mcontext.gregs[ES] = es;
113 	ucp->uc_mcontext.gregs[DS] = ds;
114 	ucp->uc_mcontext.gregs[CS] = cs;
115 	ucp->uc_mcontext.gregs[SS] = ss;
116 
117 	/*
118 	 * Yuck.
119 	 * Use unused kernel pointer field in ucontext
120 	 * to pass down self pointer and set %gs selector
121 	 * value so __lwp_create() can setup %gs atomically.
122 	 * Without this we would need to block all signals
123 	 * and directly call ___lwp_private() in _thrp_setup
124 	 * on the other side of __lwp_create().
125 	 */
126 	ucp->uc_mcontext.gregs[ESP] = (greg_t)ulwp;
127 	ucp->uc_mcontext.gregs[GS] = (greg_t)LWPGS_SEL;
128 
129 	/*
130 	 * Setup the top stack frame.
131 	 * If this fails, pass the problem up to the application.
132 	 */
133 	if ((stack = setup_top_frame(stk, stksize, ulwp)) == NULL)
134 		return (ENOMEM);
135 
136 	/* fill in registers of interest */
137 	ucp->uc_flags |= UC_CPU;
138 	ucp->uc_mcontext.gregs[EIP] = (greg_t)func;
139 	ucp->uc_mcontext.gregs[UESP] = (greg_t)stack;
140 	ucp->uc_mcontext.gregs[EBP] = (greg_t)(stack + 3);
141 
142 	return (0);
143 }
144 
145 /*
146  * Machine-dependent startup code for a newly-created thread.
147  */
148 void *
149 _thrp_setup(ulwp_t *self)
150 {
151 	self->ul_ustack.ss_sp = (void *)(self->ul_stktop - self->ul_stksiz);
152 	self->ul_ustack.ss_size = self->ul_stksiz;
153 	self->ul_ustack.ss_flags = 0;
154 	(void) setustack(&self->ul_ustack);
155 
156 	update_sched(self);
157 	tls_setup();
158 
159 	/* signals have been deferred until now */
160 	sigon(self);
161 
162 	if (self->ul_cancel_pending == 2 && !self->ul_cancel_disabled)
163 		return (NULL);	/* cancelled by pthread_create() */
164 	return (self->ul_startpc(self->ul_startarg));
165 }
166 
167 void
168 _fpinherit(ulwp_t *ulwp)
169 {
170 	ulwp->ul_fpuenv.ftag = 0xffffffff;
171 }
172 
173 void
174 getgregs(ulwp_t *ulwp, gregset_t rs)
175 {
176 	lwpstatus_t status;
177 
178 	if (getlwpstatus(ulwp->ul_lwpid, &status) == 0) {
179 		rs[EIP] = status.pr_reg[EIP];
180 		rs[EDI] = status.pr_reg[EDI];
181 		rs[ESI] = status.pr_reg[ESI];
182 		rs[EBP] = status.pr_reg[EBP];
183 		rs[EBX] = status.pr_reg[EBX];
184 		rs[UESP] = status.pr_reg[UESP];
185 	} else {
186 		rs[EIP] = 0;
187 		rs[EDI] = 0;
188 		rs[ESI] = 0;
189 		rs[EBP] = 0;
190 		rs[EBX] = 0;
191 		rs[UESP] = 0;
192 	}
193 }
194 
195 void
196 setgregs(ulwp_t *ulwp, gregset_t rs)
197 {
198 	lwpstatus_t status;
199 
200 	if (getlwpstatus(ulwp->ul_lwpid, &status) == 0) {
201 		status.pr_reg[EIP] = rs[EIP];
202 		status.pr_reg[EDI] = rs[EDI];
203 		status.pr_reg[ESI] = rs[ESI];
204 		status.pr_reg[EBP] = rs[EBP];
205 		status.pr_reg[EBX] = rs[EBX];
206 		status.pr_reg[UESP] = rs[UESP];
207 		(void) putlwpregs(ulwp->ul_lwpid, status.pr_reg);
208 	}
209 }
210 
211 int
212 __csigsetjmp(greg_t cs, greg_t ss, greg_t gs,
213 	greg_t fs, greg_t es, greg_t ds,
214 	greg_t edi, greg_t esi, greg_t ebp, greg_t esp,
215 	greg_t ebx, greg_t edx, greg_t ecx, greg_t eax, greg_t eip,
216 	sigjmp_buf env, int savemask)
217 {
218 	ucontext_t *ucp = (ucontext_t *)env;
219 	ulwp_t *self = curthread;
220 
221 	ucp->uc_link = self->ul_siglink;
222 	if (self->ul_ustack.ss_flags & SS_ONSTACK)
223 		ucp->uc_stack = self->ul_ustack;
224 	else {
225 		ucp->uc_stack.ss_sp =
226 		    (void *)(self->ul_stktop - self->ul_stksiz);
227 		ucp->uc_stack.ss_size = self->ul_stksiz;
228 		ucp->uc_stack.ss_flags = 0;
229 	}
230 	ucp->uc_flags = UC_STACK | UC_CPU;
231 	if (savemask) {
232 		ucp->uc_flags |= UC_SIGMASK;
233 		enter_critical(self);
234 		ucp->uc_sigmask = self->ul_sigmask;
235 		exit_critical(self);
236 	}
237 	ucp->uc_mcontext.gregs[GS] = gs;
238 	ucp->uc_mcontext.gregs[FS] = fs;
239 	ucp->uc_mcontext.gregs[ES] = es;
240 	ucp->uc_mcontext.gregs[DS] = ds;
241 	ucp->uc_mcontext.gregs[EDI] = edi;
242 	ucp->uc_mcontext.gregs[ESI] = esi;
243 	ucp->uc_mcontext.gregs[EBP] = ebp;
244 	ucp->uc_mcontext.gregs[ESP] = esp + 4;
245 	ucp->uc_mcontext.gregs[EBX] = ebx;
246 	ucp->uc_mcontext.gregs[EDX] = edx;
247 	ucp->uc_mcontext.gregs[ECX] = ecx;
248 	ucp->uc_mcontext.gregs[EAX] = eax;
249 	ucp->uc_mcontext.gregs[TRAPNO] = 0;
250 	ucp->uc_mcontext.gregs[ERR] = 0;
251 	ucp->uc_mcontext.gregs[EIP] = eip;
252 	ucp->uc_mcontext.gregs[CS] = cs;
253 	ucp->uc_mcontext.gregs[EFL] = 0;
254 	ucp->uc_mcontext.gregs[UESP] = esp + 4;
255 	ucp->uc_mcontext.gregs[SS] = ss;
256 
257 	return (0);
258 }
259 
260 void
261 smt_pause(void)
262 {
263 	SMT_PAUSE();
264 }
265