1 #include <sys/cdefs.h>
2 #include <namespace.h>
3 #include <lib.h>
4 #include <machine/stackframe.h>
5 #include <sys/cdefs.h>
6 #include <ucontext.h>
7 #include <signal.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <stdio.h>
13
14 void ctx_start(void (*)(void), int, ...);
15
16 /*===========================================================================*
17 * setuctx *
18 *===========================================================================*/
setuctx(const ucontext_t * ucp)19 int setuctx(const ucontext_t *ucp)
20 {
21 int r;
22
23 if (ucp == NULL) {
24 errno = EFAULT;
25 return(-1);
26 }
27
28 if (!(ucp->uc_flags & _UC_IGNSIGM)) {
29 /* Set signal mask */
30 if ((r = sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL)) == -1)
31 return(r);
32 }
33
34 if (!(ucp->uc_flags & _UC_IGNFPU)) {
35 if ((r = setmcontext(&(ucp->uc_mcontext))) == -1)
36 return(r);
37 }
38
39 return(0);
40 }
41
42
43 /*===========================================================================*
44 * getuctx *
45 *===========================================================================*/
getuctx(ucontext_t * ucp)46 int getuctx(ucontext_t *ucp)
47 {
48 int r;
49
50 if (ucp == NULL) {
51 errno = EFAULT;
52 return(-1);
53 }
54
55 if (!(ucp->uc_flags & _UC_IGNSIGM)) {
56 /* Get signal mask */
57 if ((r = sigprocmask(0, NULL, &ucp->uc_sigmask)) == -1)
58 return(r);
59 }
60
61 if (!(ucp->uc_flags & _UC_IGNFPU)) {
62 if ((r = getmcontext(&(ucp->uc_mcontext))) != 0)
63 return(r);
64 }
65
66 return(0);
67 }
68
69
70 /*===========================================================================*
71 * makecontext *
72 *===========================================================================*/
makecontext(ucontext_t * ucp,void (* func)(void),int argc,...)73 void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
74 {
75 va_list ap;
76 unsigned int *stack_top;
77
78 /* There are a number of situations that are erroneous, but we can't actually
79 tell the caller something is wrong, because this is a void function.
80 Instead, mcontext_t contains a magic field that has to be set
81 properly before it can be used. */
82 if (ucp == NULL) {
83 return;
84 } else if ((ucp->uc_stack.ss_sp == NULL) ||
85 (ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
86 ucp->uc_mcontext.mc_magic = 0;
87 _UC_MACHINE_SET_STACK(ucp, 0);
88 return;
89 }
90
91 if (ucp->uc_mcontext.mc_magic == MCF_MAGIC) {
92 #if defined(__i386__)
93 /* The caller provides a pointer to a stack that we can use to run our
94 context on. When the context starts, control is given to a wrapped
95 start routine, which calls a function and cleans up the stack
96 afterwards. The wrapper needs the address of that function on the
97 stack.
98 The stack will be prepared as follows:
99 func() - start routine
100 arg1 - first argument
101 ...
102 argn - last argument
103 ucp - context, esp points here when `func' returns
104 _ctx_start pops the address of `func' from the stack and calls it.
105 The stack will then be setup with all arguments for `func'. When
106 `func' returns, _ctx_start cleans up the stack such that ucp is at
107 the top of the stack, ready to be used by resumecontext.
108 Resumecontext, in turn, checks whether another context is ready to
109 be executed (i.e., uc_link != NULL) or exit(2)s the process. */
110
111 /* Find the top of the stack from which we grow downwards. */
112 stack_top = (unsigned int *) ((uintptr_t ) ucp->uc_stack.ss_sp +
113 ucp->uc_stack.ss_size);
114
115 /* Align the arguments to 16 bytes (we might lose a few bytes of stack
116 space here).*/
117 stack_top = (unsigned int *) ((uintptr_t) stack_top & ~0xf);
118
119 /* Make room for 'func', the `func' routine arguments, and ucp. */
120 stack_top -= (1 + argc + 1);
121
122 /* Adjust the machine context to point to the top of this stack and the
123 program counter to the context start wrapper. */
124 _UC_MACHINE_SET_EBP(ucp, 0); /* Clear frame pointer */
125 _UC_MACHINE_SET_STACK(ucp, (reg_t) stack_top);
126 _UC_MACHINE_SET_PC(ucp, (reg_t) ctx_start);
127
128 *stack_top++ = (uintptr_t) func;
129
130 /* Copy arguments to the stack. */
131 va_start(ap, argc);
132 while (argc-- > 0) {
133 *stack_top++ = va_arg(ap, uintptr_t);
134 }
135 va_end(ap);
136
137 /* Store ucp on the stack */
138 *stack_top = (uintptr_t) ucp;
139
140 /* Set ESI to point to the base of the stack where ucp is stored, so
141 that the wrapper function knows how to clean up the stack after
142 calling `func' (i.e., how to adjust ESP). */
143 _UC_MACHINE_SET_ESI(ucp, (reg_t) stack_top);
144
145
146 /* If we ran out of stack space, invalidate stack pointer. Eventually,
147 swapcontext will choke on this and return ENOMEM. */
148 if (stack_top == ucp->uc_stack.ss_sp) {
149 _UC_MACHINE_SET_STACK(ucp, 0);
150 }
151 #elif defined(__arm__)
152 /* The caller provides a pointer to a stack that we can use to run our
153 context on. When the context starts, control is given to the
154 requested function. When the function finishes, it returns to the
155 _ctx_start wrapper that calls resumecontext (after setting up
156 resumecontext's parameter).
157
158 The first four arguments for the function will be passed in
159 regs r0-r3 as specified by the ABI, and the rest will go on
160 the stack. The ucp is saved in r4 so that we can
161 eventually pass it to resumecontext. The r4 register is
162 callee-preserved, so the ucp will remain valid in r4 when
163 _ctx_start runs. _ctx_start will move the ucp from r4 into
164 r0, so that the ucp is the first paramater for resumecontext.
165 Then, _ctx_start will call resumecontext. Resumecontext, in turn,
166 checks whether another context is ready to be executed
167 (i.e., uc_link != NULL) or exit(2)s the process. */
168
169 /* Find the top of the stack from which we grow downwards. */
170 stack_top = (unsigned int *) ((uintptr_t ) ucp->uc_stack.ss_sp +
171 ucp->uc_stack.ss_size);
172
173 /* Align the arguments to 16 bytes (we might lose a few bytes of stack
174 space here).*/
175 stack_top = (unsigned int *) ((uintptr_t) stack_top & ~0xf);
176
177 /* Make room for `func' routine arguments that don't fit in r0-r3 */
178 if (argc > 4)
179 stack_top -= argc - 4;
180
181 /* Adjust the machine context to point to the top of this stack and the
182 program counter to the 'func' entry point. Set lr to ctx_start, so
183 ctx_start runs after 'func'. Save ucp in r4 */
184 _UC_MACHINE_SET_FP(ucp, 0); /* Clear frame pointer */
185 _UC_MACHINE_SET_STACK(ucp, (reg_t) stack_top);
186 _UC_MACHINE_SET_PC(ucp, (reg_t) func);
187 _UC_MACHINE_SET_LR(ucp, (reg_t) ctx_start);
188 _UC_MACHINE_SET_R4(ucp, (reg_t) ucp);
189
190 /* Copy arguments to r0-r3 and stack. */
191 va_start(ap, argc);
192 /* Pass up to four arguments in registers. */
193 if (argc-- > 0)
194 _UC_MACHINE_SET_R0(ucp, va_arg(ap, uintptr_t));
195 if (argc-- > 0)
196 _UC_MACHINE_SET_R1(ucp, va_arg(ap, uintptr_t));
197 if (argc-- > 0)
198 _UC_MACHINE_SET_R2(ucp, va_arg(ap, uintptr_t));
199 if (argc-- > 0)
200 _UC_MACHINE_SET_R3(ucp, va_arg(ap, uintptr_t));
201 /* Pass the rest on the stack. */
202 while (argc-- > 0) {
203 *stack_top++ = va_arg(ap, uintptr_t);
204 }
205 va_end(ap);
206
207 /* If we ran out of stack space, invalidate stack pointer. Eventually,
208 swapcontext will choke on this and return ENOMEM. */
209 if (stack_top == ucp->uc_stack.ss_sp) {
210 _UC_MACHINE_SET_STACK(ucp, 0);
211 }
212 #else
213 # error "Unsupported platform"
214 #endif
215 }
216 }
217
218
219 /*===========================================================================*
220 * swapcontext *
221 *===========================================================================*/
swapcontext(ucontext_t * oucp,const ucontext_t * ucp)222 int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
223 {
224 int r;
225
226 if ((oucp == NULL) || (ucp == NULL)) {
227 errno = EFAULT;
228 return(-1);
229 }
230
231 if (_UC_MACHINE_STACK(ucp) == 0) {
232 /* No stack space. Bail out. */
233 errno = ENOMEM;
234 return(-1);
235 }
236
237 oucp->uc_flags &= ~_UC_SWAPPED;
238 r = getcontext(oucp);
239 if ((r == 0) && !(oucp->uc_flags & _UC_SWAPPED)) {
240 oucp->uc_flags |= _UC_SWAPPED;
241 r = setcontext(ucp);
242 }
243
244 return(r);
245 }
246
247
248 /*===========================================================================*
249 * resumecontext *
250 *===========================================================================*/
251 __dead
resumecontext(ucontext_t * ucp)252 void resumecontext(ucontext_t *ucp)
253 {
254 if (ucp->uc_link == NULL) exit(0);
255
256 /* Error handling? Where should the error go to? */
257 (void) setcontext((const ucontext_t *) ucp->uc_link);
258
259 exit(1); /* Never reached */
260 }
261
262