1 /* Copyright (C) 2001-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ucontext.h>
23 #include <unistd.h>
24 #include <link.h>
25 #include <elf.h>
26 #include <fpu_control.h>
27 #include <sys/auxv.h>
28 #include <support/support.h>
29 
30 static ucontext_t ctx[3];
31 
32 
33 volatile int global;
34 
35 
36 static int back_in_main;
37 
38 
39 volatile static ElfW(auxv_t) *auxv = NULL;
40 
query_auxv(int type)41 ElfW(Addr) query_auxv(int type)
42 {
43   FILE *auxv_f;
44   ElfW(auxv_t) auxv_struct;
45   ElfW(auxv_t) *auxv_temp;
46   int i = 0;
47 
48   /* if the /proc/self/auxv file has not been manually copied into the heap
49      yet, then do it */
50 
51   if(auxv == NULL)
52     {
53       auxv_f = fopen("/proc/self/auxv", "r");
54 
55       if(auxv_f == 0)
56 	{
57 	  perror("Error opening file for reading");
58 	  return 0;
59 	}
60       auxv = xmalloc (getpagesize ());
61 
62       do
63 	{
64 	  fread (&auxv_struct, sizeof (ElfW(auxv_t)), 1, auxv_f);
65 	  auxv[i] = auxv_struct;
66 	  i++;
67 	} while(auxv_struct.a_type != AT_NULL);
68     }
69 
70   auxv_temp = (ElfW(auxv_t) *)auxv;
71   i = 0;
72   do
73     {
74       if(auxv_temp[i].a_type == type)
75 	{
76 	  return auxv_temp[i].a_un.a_val;
77 	}
78       i++;
79     } while (auxv_temp[i].a_type != AT_NULL);
80 
81   return 0;
82 }
83 
84 typedef unsigned int di_fpscr_t __attribute__ ((__mode__ (__DI__)));
85 typedef unsigned int si_fpscr_t __attribute__ ((__mode__ (__SI__)));
86 
87 #define _FPSCR_RESERVED 0xfffffff8ffffff04ULL
88 
89 #define _FPSCR_TEST0_DRN 0x0000000400000000ULL
90 #define _FPSCR_TEST0_RN  0x0000000000000003ULL
91 
92 #define _FPSCR_TEST1_DRN 0x0000000300000000ULL
93 #define _FPSCR_TEST1_RN  0x0000000000000002ULL
94 
95 /* Macros for accessing the hardware control word on Power6[x].  */
96 #define _GET_DI_FPSCR(__fpscr)						\
97   ({union { double d; di_fpscr_t fpscr; } u;				\
98     u.d = __builtin_mffs ();						\
99     (__fpscr) = u.fpscr;						\
100     u.fpscr;								\
101   })
102 
103 /* We make sure to zero fp after we use it in order to prevent stale data
104    in an fp register from making a test-case pass erroneously.  */
105 # define _SET_DI_FPSCR(__fpscr)						\
106   { union { double d; di_fpscr_t fpscr; } u;				\
107     register double fr;							\
108     u.fpscr = __fpscr;							\
109     fr = u.d;								\
110     /* Set the entire 64-bit FPSCR.  */					\
111     __asm__ (".machine push; "						\
112 	     ".machine \"power6\"; "					\
113 	     "mtfsf 255,%0,1,0; "					\
114 	     ".machine pop" : : "f" (fr));				\
115     fr = 0.0;								\
116   }
117 
118 # define _GET_SI_FPSCR(__fpscr)						\
119   ({union { double d; di_fpscr_t fpscr; } u;				\
120     u.d = __builtin_mffs ();						\
121     (__fpscr) = (si_fpscr_t) u.fpscr;					\
122     (si_fpscr_t) u.fpscr;						\
123   })
124 
125 /* We make sure to zero fp after we use it in order to prevent stale data
126    in an fp register from making a test-case pass erroneously.  */
127 # define _SET_SI_FPSCR(__fpscr)						\
128   { union { double d; di_fpscr_t fpscr; } u;				\
129     register double fr;							\
130     /* More-or-less arbitrary; this is a QNaN. */			\
131     u.fpscr = 0xfff80000ULL << 32;					\
132     u.fpscr |= __fpscr & 0xffffffffULL;					\
133     fr = u.d;								\
134     __builtin_mtfsf (255, fr);						\
135     fr = 0.0;								\
136   }
137 
prime_special_regs(int which)138 void prime_special_regs(int which)
139 {
140   ElfW(Addr) a_val;
141 
142   di_fpscr_t di_fpscr __attribute__ ((__aligned__(8)));
143 
144   a_val = query_auxv(AT_HWCAP);
145   if(a_val == -1)
146     {
147       puts ("querying the auxv for the hwcap failed");
148       _exit (1);
149     }
150 
151   /* Indicates a 64-bit FPSCR.  */
152   if (a_val & PPC_FEATURE_HAS_DFP)
153     {
154       _GET_DI_FPSCR(di_fpscr);
155 
156       /* Overwrite the existing DRN and RN if there is one.  */
157       if (which == 0)
158         di_fpscr = ((di_fpscr & _FPSCR_RESERVED) | (_FPSCR_TEST0_DRN | _FPSCR_TEST0_RN));
159       else
160         di_fpscr = ((di_fpscr & _FPSCR_RESERVED) | (_FPSCR_TEST1_DRN | _FPSCR_TEST1_RN));
161       puts ("Priming 64-bit FPSCR with:");
162       printf("0x%.16llx\n",(unsigned long long int)di_fpscr);
163 
164       _SET_DI_FPSCR(di_fpscr);
165     }
166   else
167     {
168       puts ("32-bit FPSCR found and will be tested.");
169       _GET_SI_FPSCR(di_fpscr);
170 
171       /* Overwrite the existing RN if there is one.  */
172       if (which == 0)
173         di_fpscr = ((di_fpscr & _FPSCR_RESERVED) | (_FPSCR_TEST0_RN));
174       else
175         di_fpscr = ((di_fpscr & _FPSCR_RESERVED) | (_FPSCR_TEST1_RN));
176       puts ("Priming 32-bit FPSCR with:");
177       printf("0x%.8lx\n",(unsigned long int) di_fpscr);
178 
179       _SET_SI_FPSCR(di_fpscr);
180     }
181 }
182 
clear_special_regs(void)183 void clear_special_regs(void)
184 {
185   ElfW(Addr) a_val;
186 
187   di_fpscr_t di_fpscr __attribute__ ((__aligned__(8)));
188 
189   union {
190 	  double d;
191 	  unsigned long long int lli;
192 	  unsigned int li[2];
193   } dlli;
194 
195   a_val = query_auxv(AT_HWCAP);
196   if(a_val == -1)
197     {
198       puts ("querying the auxv for the hwcap failed");
199       _exit (1);
200     }
201 
202 #if __WORDSIZE == 32
203   dlli.d = ctx[0].uc_mcontext.uc_regs->fpregs.fpscr;
204 #else
205   dlli.d = ctx[0].uc_mcontext.fp_regs[32];
206 #endif
207 
208   puts("The FPSCR value saved in the ucontext_t is:");
209 
210   /* Indicates a 64-bit FPSCR.  */
211   if (a_val & PPC_FEATURE_HAS_DFP)
212     {
213       printf("0x%.16llx\n",dlli.lli);
214       di_fpscr = 0x0;
215       puts ("Clearing the 64-bit FPSCR to:");
216       printf("0x%.16llx\n",(unsigned long long int) di_fpscr);
217 
218       _SET_DI_FPSCR(di_fpscr);
219     }
220   else
221     {
222       printf("0x%.8x\n",(unsigned int) dlli.li[1]);
223       di_fpscr = 0x0;
224       puts ("Clearing the 32-bit FPSCR to:");
225       printf("0x%.8lx\n",(unsigned long int) di_fpscr);
226 
227       _SET_SI_FPSCR(di_fpscr);
228     }
229 }
230 
test_special_regs(int which)231 void test_special_regs(int which)
232 {
233   ElfW(Addr) a_val;
234   unsigned long long int test;
235 
236   di_fpscr_t di_fpscr __attribute__ ((__aligned__(8)));
237 
238   a_val = query_auxv(AT_HWCAP);
239   if(a_val == -1)
240     {
241       puts ("querying the auxv for the hwcap failed");
242       _exit (2);
243     }
244 
245   /* Indicates a 64-bit FPSCR.  */
246   if (a_val & PPC_FEATURE_HAS_DFP)
247     {
248       _GET_DI_FPSCR(di_fpscr);
249 
250       if (which == 0)
251 	puts ("After setcontext the 64-bit FPSCR contains:");
252       else
253 	puts ("After swapcontext the 64-bit FPSCR contains:");
254 
255       printf("0x%.16llx\n",(unsigned long long int) di_fpscr);
256       test = (_FPSCR_TEST0_DRN | _FPSCR_TEST0_RN);
257       if((di_fpscr & (test)) != (test))
258         {
259 	  printf ("%s: DRN and RN bits set before getcontext were not preserved across [set|swap]context call: %m",__FUNCTION__);
260 	  _exit (3);
261         }
262     }
263   else
264     {
265       _GET_SI_FPSCR(di_fpscr);
266       if (which == 0)
267 	puts ("After setcontext the 32-bit FPSCR contains:");
268       else
269 	puts ("After swapcontext the 32-bit FPSCR contains:");
270 
271       printf("0x%.8lx\n",(unsigned long int) di_fpscr);
272       test = _FPSCR_TEST0_RN;
273       if((di_fpscr & test) != test)
274         {
275 	  printf ("%s: RN bit set before getcontext was not preserved across [set|swap]context call: %m",__FUNCTION__);
276 	  _exit (4);
277         }
278     }
279 }
280 
281 
282 static void
check_called(void)283 check_called (void)
284 {
285   if (back_in_main == 0)
286     {
287       puts ("program did not reach main again");
288       _exit (5);
289     }
290 }
291 
292 
293 int
main(void)294 main (void)
295 {
296   atexit (check_called);
297 
298   puts ("priming the FPSCR with a marker");
299   prime_special_regs (0);
300 
301   puts ("making contexts");
302   if (getcontext (&ctx[0]) != 0)
303     {
304       if (errno == ENOSYS)
305 	{
306 	  back_in_main = 1;
307 	  exit (0);
308 	}
309 
310       printf ("%s: getcontext: %m\n", __FUNCTION__);
311       exit (6);
312     }
313 
314   /* Play some tricks with this context.  */
315   if (++global == 1)
316     {
317     clear_special_regs ( );
318     if (setcontext (&ctx[0]) != 0)
319       {
320 	printf ("%s: setcontext: %m\n", __FUNCTION__);
321 	exit (7);
322       }
323     }
324   if (global != 2)
325     {
326       printf ("%s: 'global' not incremented twice\n", __FUNCTION__);
327       exit (8);
328     }
329 
330   test_special_regs (0);
331 
332   global = 0;
333   if (getcontext (&ctx[0]) != 0)
334     {
335       printf ("%s: getcontext: %m\n", __FUNCTION__);
336       exit (9);
337     }
338 
339   if (++global == 1)
340     {
341       puts ("priming the FPSCR with a marker");
342       prime_special_regs (1);
343 
344       puts ("swapping contexts");
345       if (swapcontext (&ctx[1], &ctx[0]) != 0)
346         {
347           printf ("%s: swapcontext: %m\n", __FUNCTION__);
348           exit (9);
349         }
350     }
351   if (global != 2)
352     {
353       printf ("%s: 'global' not incremented twice\n", __FUNCTION__);
354       exit (10);
355     }
356 
357   test_special_regs (1);
358 
359   puts ("back at main program");
360   back_in_main = 1;
361 
362   puts ("test succeeded");
363   return 0;
364 }
365