xref: /minix3/minix/lib/libc/sys/_ucontext.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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