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