1 | /* Copyright (C) 2001-2024 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 | #include <support/xstdio.h> |
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(filename: "/proc/self/auxv" , modes: "r" ); |
56 | |
57 | if(auxv_f == 0) |
58 | { |
59 | perror(s: "Error opening file for reading" ); |
60 | return 0; |
61 | } |
62 | auxv = xmalloc (n: getpagesize ()); |
63 | |
64 | do |
65 | { |
66 | xfread (ptr: &auxv_struct, size: sizeof (ElfW(auxv_t)), nmemb: 1, stream: auxv_f); |
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 | |
86 | typedef unsigned int di_fpscr_t __attribute__ ((__mode__ (__DI__))); |
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]. */ |
98 | #define _GET_DI_FPSCR(__fpscr) \ |
99 | ({union { double d; di_fpscr_t fpscr; } u; \ |
100 | u.d = __builtin_mffs (); \ |
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 |
106 | in an fp register from making a test-case pass erroneously. */ |
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; \ |
122 | u.d = __builtin_mffs (); \ |
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 |
128 | in an fp register from making a test-case pass erroneously. */ |
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; \ |
136 | __builtin_mtfsf (255, fr); \ |
137 | fr = 0.0; \ |
138 | } |
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 (s: "querying the auxv for the hwcap failed" ); |
150 | _exit (status: 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 (s: "Priming 64-bit FPSCR with:" ); |
164 | printf(format: "0x%.16llx\n" ,(unsigned long long int)di_fpscr); |
165 | |
166 | _SET_DI_FPSCR(di_fpscr); |
167 | } |
168 | else |
169 | { |
170 | puts (s: "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 (s: "Priming 32-bit FPSCR with:" ); |
179 | printf(format: "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 (s: "querying the auxv for the hwcap failed" ); |
201 | _exit (status: 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(s: "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(format: "0x%.16llx\n" ,dlli.lli); |
216 | di_fpscr = 0x0; |
217 | puts (s: "Clearing the 64-bit FPSCR to:" ); |
218 | printf(format: "0x%.16llx\n" ,(unsigned long long int) di_fpscr); |
219 | |
220 | _SET_DI_FPSCR(di_fpscr); |
221 | } |
222 | else |
223 | { |
224 | printf(format: "0x%.8x\n" ,(unsigned int) dlli.li[1]); |
225 | di_fpscr = 0x0; |
226 | puts (s: "Clearing the 32-bit FPSCR to:" ); |
227 | printf(format: "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 (s: "querying the auxv for the hwcap failed" ); |
244 | _exit (status: 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 (s: "After setcontext the 64-bit FPSCR contains:" ); |
254 | else |
255 | puts (s: "After swapcontext the 64-bit FPSCR contains:" ); |
256 | |
257 | printf(format: "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 (format: "%s: DRN and RN bits set before getcontext were not preserved across [set|swap]context call: %m" ,__FUNCTION__); |
262 | _exit (status: 3); |
263 | } |
264 | } |
265 | else |
266 | { |
267 | _GET_SI_FPSCR(di_fpscr); |
268 | if (which == 0) |
269 | puts (s: "After setcontext the 32-bit FPSCR contains:" ); |
270 | else |
271 | puts (s: "After swapcontext the 32-bit FPSCR contains:" ); |
272 | |
273 | printf(format: "0x%.8lx\n" ,(unsigned long int) di_fpscr); |
274 | test = _FPSCR_TEST0_RN; |
275 | if((di_fpscr & test) != test) |
276 | { |
277 | printf (format: "%s: RN bit set before getcontext was not preserved across [set|swap]context call: %m" ,__FUNCTION__); |
278 | _exit (status: 4); |
279 | } |
280 | } |
281 | } |
282 | |
283 | |
284 | static void |
285 | check_called (void) |
286 | { |
287 | if (back_in_main == 0) |
288 | { |
289 | puts (s: "program did not reach main again" ); |
290 | _exit (status: 5); |
291 | } |
292 | } |
293 | |
294 | |
295 | int |
296 | main (void) |
297 | { |
298 | atexit (func: check_called); |
299 | |
300 | puts (s: "priming the FPSCR with a marker" ); |
301 | prime_special_regs (which: 0); |
302 | |
303 | puts (s: "making contexts" ); |
304 | if (getcontext (ucp: &ctx[0]) != 0) |
305 | { |
306 | if (errno == ENOSYS) |
307 | { |
308 | back_in_main = 1; |
309 | exit (status: 0); |
310 | } |
311 | |
312 | printf (format: "%s: getcontext: %m\n" , __FUNCTION__); |
313 | exit (status: 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 (format: "%s: setcontext: %m\n" , __FUNCTION__); |
323 | exit (status: 7); |
324 | } |
325 | } |
326 | if (global != 2) |
327 | { |
328 | printf (format: "%s: 'global' not incremented twice\n" , __FUNCTION__); |
329 | exit (status: 8); |
330 | } |
331 | |
332 | test_special_regs (which: 0); |
333 | |
334 | global = 0; |
335 | if (getcontext (ucp: &ctx[0]) != 0) |
336 | { |
337 | printf (format: "%s: getcontext: %m\n" , __FUNCTION__); |
338 | exit (status: 9); |
339 | } |
340 | |
341 | if (++global == 1) |
342 | { |
343 | puts (s: "priming the FPSCR with a marker" ); |
344 | prime_special_regs (which: 1); |
345 | |
346 | puts (s: "swapping contexts" ); |
347 | if (swapcontext (oucp: &ctx[1], ucp: &ctx[0]) != 0) |
348 | { |
349 | printf (format: "%s: swapcontext: %m\n" , __FUNCTION__); |
350 | exit (status: 9); |
351 | } |
352 | } |
353 | if (global != 2) |
354 | { |
355 | printf (format: "%s: 'global' not incremented twice\n" , __FUNCTION__); |
356 | exit (status: 10); |
357 | } |
358 | |
359 | test_special_regs (which: 1); |
360 | |
361 | puts (s: "back at main program" ); |
362 | back_in_main = 1; |
363 | |
364 | puts (s: "test succeeded" ); |
365 | return 0; |
366 | } |
367 | |