xref: /minix/minix/tests/test51.c (revision 433d6423)
1 /* Test51.c
2  *
3  * Test getcontext, setcontext, makecontext, and swapcontext system calls.
4  *
5  * Part of this test is somewhat based on the GNU GCC ucontext test set.
6  *
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <ucontext.h>
15 #include <math.h>
16 #include <fenv.h>
17 
18 #include <sys/signal.h>
19 
20 /* We can't use a global subtest variable reliably, because threads might
21    change the value when we reenter a thread (i.e., report wrong subtest
22    number). */
23 #define err(st, en) do { subtest = st; e(en); } while(0)
24 
25 void do_calcs(void);
26 void do_child(void);
27 void do_parent(void);
28 void func1(int a, int b, int c, int d, int e, int f, int g, int h, int
29 	i, int j, int k, int l, int m, int n, int o, int p, int q, int r, int s,
30 	int t, int u, int v, int w, int x, int y, int z, int aa, int bb);
31 void func2(void);
32 void just_exit(void);
33 void test_brk(void);
34 void verify_main_reenter(void);
35 
36 int max_error = 5;
37 #include "common.h"
38 
39 #define SSIZE 32768
40 #define ROUNDS 10
41 #define SWAPS 10
42 
43 
44 int subtest;
45 ucontext_t ctx[3];
46 int entered_func1, entered_func2, reentered_main, entered_overflow;
47 
48 static char st_stack[SSIZE];
49 static volatile int shift, global;
50 
51 void do_calcs(void)
52 {
53   float a, b, c, d, e;
54   float foo, bar;
55   int i;
56 
57   a = 1.1;
58   b = 2.2;
59   c = 3.3;
60   d = 4.4;
61   e = 5.5;
62 
63   foo = a * b; /* 2.42 */
64   bar = c * d; /* 14.52 */
65 
66   i = 0;
67   while(i < ROUNDS) {
68 	foo += c; /* 5.72 */
69 	foo *= d; /* 25.168 */
70 	foo /= e; /* 4.5760 */
71 	bar -= a; /* 13.42 */
72 	bar *= b; /* 29.524 */
73 	bar /= e; /* 5.3680 */
74 
75 	/* Undo */
76 	foo *= e;
77 	foo /= d;
78 	foo -= c;
79 
80 	bar *= e;
81 	bar /= b;
82 	bar += a;
83 
84 	i++;
85   }
86   if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
87   if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
88 }
89 
90 void do_child(void)
91 {
92   int s;
93   s = 1;
94 
95   /* Initialize FPU context and verify it's set to round to nearest. */
96   if (fegetround() != FE_TONEAREST) err(9, 1);
97 
98   /* Now we change the rounding to something else, and this should be preserved
99      between context swaps. */
100   if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
101 
102   while(s < SWAPS) {
103 	s++;
104 	if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
105 	do_calcs();
106 	if (fegetround() != FE_DOWNWARD) err(9, 4);
107   }
108   quit();
109 }
110 
111 void do_parent(void)
112 {
113   ucontext_t dummy;
114   int s;
115   s = 1;
116 
117   /* Initialize FPU context and verify it's set to round to nearest. */
118   if (fegetround() != FE_TONEAREST) err(10, 1);
119 
120   /* Now we change the rounding to something else, and this should be preserved
121      between context swaps. */
122   if (fesetround(FE_UPWARD) != 0) err(10, 2);
123 
124   /* Quick check to make sure that getcontext does not reset the FPU state. */
125   getcontext(&dummy);
126 
127   if (fegetround() != FE_UPWARD) err(10, 3);
128 
129   while(s < SWAPS) {
130 	do_calcs();
131 	if (fegetround() != FE_UPWARD) err(10, 4);
132 	s++;
133 	if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 5);
134   }
135   /* Returning to main thread through uc_link */
136 }
137 
138 static void fail(void)
139 {
140   /* Shouldn't get here */
141   err(5, 1);
142 }
143 
144 void func1(int a, int b, int c, int d, int e, int f, int g,
145 	   int h, int i, int j, int k, int l, int m, int n,
146 	   int o, int p, int q, int r, int s, int t, int u,
147 	   int v, int w, int x, int y, int z, int aa, int bb)
148 {
149   if ( a != (0x0000001 << shift) ||  b != (0x0000004 << shift) ||
150        c != (0x0000010 << shift) ||  d != (0x0000040 << shift) ||
151        e != (0x0000100 << shift) ||  f != (0x0000400 << shift) ||
152        g != (0x0001000 << shift) ||  h != (0x0004000 << shift) ||
153        i != (0x0010000 << shift) ||  j != (0x0040000 << shift) ||
154        k != (0x0100000 << shift) ||  l != (0x0400000 << shift) ||
155        m != (0x1000000 << shift) ||  n != (0x4000000 << shift) ||
156        o != (0x0000002 << shift) ||  p != (0x0000008 << shift) ||
157        q != (0x0000020 << shift) ||  r != (0x0000080 << shift) ||
158        s != (0x0000200 << shift) ||  t != (0x0000800 << shift) ||
159        u != (0x0002000 << shift) ||  v != (0x0008000 << shift) ||
160        w != (0x0020000 << shift) ||  x != (0x0080000 << shift) ||
161        y != (0x0200000 << shift) ||  z != (0x0800000 << shift) ||
162       aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
163 	err(2, 1);
164   }
165 
166   if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
167   shift++;
168   entered_func1++;
169 }
170 
171 void func2(void)
172 {
173   if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
174   entered_func2++;
175 }
176 
177 void just_exit(void)
178 {
179   if (errct == 0) printf("ok\n");
180   _exit(1);
181 }
182 
183 void test_brk(void)
184 {
185   char *big_stack;
186 
187   big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
188   /* If this fails, it is likely brk system call failed due stack/data segments
189      collision detection. Unless the system really is low on memory, this is an
190      error. */
191   if (big_stack == NULL) err(7, 1);
192 }
193 
194 void verify_main_reenter(void)
195 {
196   if (reentered_main == 0) err(4, 1);
197 }
198 
199 int set_context_test_value;
200 void set_context_test_thread1(void){
201 	set_context_test_value |= 0x1;
202 	setcontext(&ctx[2]);
203 	err(1, 24);
204 
205 }
206 
207 void set_context_test_thread2(void){
208 	set_context_test_value |= 0x1 << 1;
209 	setcontext(&ctx[0]);
210 	err(1, 23);
211 }
212 
213 int main(void)
214 {
215   start(51);
216 
217   atexit(verify_main_reenter);
218 
219   /* Save current context in ctx[0] */
220   if (getcontext(&ctx[0]) != 0) {
221 	/* Don't verify reentering main, not going to happen */
222 	atexit(just_exit);
223 	err(1, 1);
224   }
225 
226   ctx[1] = ctx[0];
227   ctx[1].uc_stack.ss_sp = st_stack;
228   ctx[1].uc_stack.ss_size = SSIZE;
229   ctx[1].uc_link = &ctx[0]; /* When done running, return here */
230 
231   /* ctx[1] is going to run func1 and then return here (uc_link). */
232   /* We'll see later on whether makecontext worked. */
233   makecontext(&ctx[1], (void (*) (void)) func1, 28,
234   	      (0x0000001 << shift), (0x0000004 << shift),
235 	      (0x0000010 << shift), (0x0000040 << shift),
236 	      (0x0000100 << shift), (0x0000400 << shift),
237 	      (0x0001000 << shift), (0x0004000 << shift),
238 	      (0x0010000 << shift), (0x0040000 << shift),
239 	      (0x0100000 << shift), (0x0400000 << shift),
240 	      (0x1000000 << shift), (0x4000000 << shift),
241 	      (0x0000002 << shift), (0x0000008 << shift),
242 	      (0x0000020 << shift), (0x0000080 << shift),
243 	      (0x0000200 << shift), (0x0000800 << shift),
244 	      (0x0002000 << shift), (0x0008000 << shift),
245 	      (0x0020000 << shift), (0x0080000 << shift),
246 	      (0x0200000 << shift), (0x0800000 << shift),
247 	      (0x2000000 << shift), (0x8000000 << shift));
248 
249   if (++global == 1) {
250 	/* First time we're here. Let's run ctx[1] and return to ctx[0] when
251 	 * we're done. Note that we return to above the 'makecontext' call. */
252 	if (setcontext(&ctx[1]) != 0) err(1, 2);
253   }
254   if (global != 2) {
255 	/* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
256 	   above ++global is executed again and should've become 2. */
257 	err(1, 3);
258   }
259 
260   /* Setup ctx[2] to run func2 */
261   if (getcontext(&ctx[2]) != 0) err(1, 4);
262   ctx[2].uc_stack.ss_sp = malloc(SSIZE);
263   ctx[2].uc_stack.ss_size = SSIZE;
264   ctx[2].uc_link = &ctx[1];
265   makecontext(&ctx[2], (void (*) (void)) func2, 0);
266 
267   /* Now things become tricky. ctx[2] is set up such that when it finishes
268      running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
269      when func2 has finished running, we continue with ctx[1] and, finally, we
270      return to ctx[0]. */
271   if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
272   reentered_main = 1;
273 
274   /* The call graph is as follows:
275    *
276    *                       ########
277    *             /--------># main #
278    *             7    /----########----\
279    *             |    |    ^           |
280    *             |    1    2           3
281    *             |    V    |           V
282    *          #########----/           #########
283    *          # func1 #<-------4-------# func2 #
284    *          #########--------5------>#########
285    *                 ^                  |
286    *                 |                  |
287    *                 \---------6--------/
288    *
289    * Main calls func1, func1 increases entered_func1, and returns to main. Main
290    * calls func2, swaps to func1, swaps to func2, which increases entered_func2,
291    * continues with func1, which increases entered_func1 again, continues to
292    * main, where reentered_main is set to 1. In effect, entered_func1 == 2,
293    * entered_func2 == 1, reentered_main == 1. Verify that. */
294 
295   if (entered_func1 != 2) err(1, 6);
296   if (entered_func2 != 1) err(1, 7);
297   /* reentered_main == 1 is verified upon exit */
298 
299   /* Try to allocate too small a stack */
300   free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
301   if (getcontext(&ctx[2]) != 0) err(1, 8);
302   ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
303   ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
304   ctx[2].uc_link = &ctx[0];
305   makecontext(&ctx[2], (void (*) (void)) fail, 0);
306   /* Because makecontext is void, we can only detect an error by trying to use
307      the invalid context */
308   if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
309 
310   /* Try to allocate a huge stack to force the usage of brk/sbrk system call
311      to enlarge the data segment. Because we are fiddling with the stack
312      pointer, the OS might think the stack segment and data segment have
313      collided and kill us. This is wrong and therefore the following should
314      work. */
315   free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
316   if (getcontext(&ctx[2]) != 0) err(1, 14);
317   ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
318   ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
319   ctx[2].uc_link = &ctx[0];
320   makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
321   if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
322 
323   ctx[1].uc_link = &ctx[0];
324   ctx[2].uc_link = NULL;
325   makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
326   makecontext(&ctx[2], (void (*) (void)) do_child, 0);
327   if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
328 
329   /* test setcontext  first do some cleanup */
330 
331   free(ctx[1].uc_stack.ss_sp);
332   free(ctx[2].uc_stack.ss_sp);
333 
334   memset(&ctx[0],0,sizeof(ucontext_t));
335   memset(&ctx[1],0,sizeof(ucontext_t));
336   memset(&ctx[2],0,sizeof(ucontext_t));
337 
338 
339   /* create 3 new contexts */
340   volatile int cb =1;
341 
342   /* control will be returned here */
343   set_context_test_value  = 0;
344   if (getcontext(&ctx[0]) != 0) err(1, 17);
345   if (set_context_test_value != 0x3) err(1, 20);
346   if (cb == 1) {
347     cb =0;
348     if (getcontext(&ctx[1]) != 0) err(1, 18);
349     if (getcontext(&ctx[2]) != 0) err(1, 19);
350 
351     // allocate new stacks */
352     ctx[1].uc_stack.ss_sp = malloc(SSIZE);
353     ctx[1].uc_stack.ss_size = SSIZE;
354     ctx[2].uc_stack.ss_sp = malloc(SSIZE);
355     ctx[2].uc_stack.ss_size = SSIZE;
356 
357     makecontext(&ctx[1],set_context_test_thread1,0);
358     makecontext(&ctx[2],set_context_test_thread2,0);
359     if( setcontext(&ctx[1]) != 0){
360       err(1, 21);
361     }
362     err(1, 22);
363   }
364 
365   quit();
366   return(-1);
367 }
368 
369