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
32static ucontext_t ctx[3];
33
34
35volatile int global;
36
37
38static int back_in_main;
39
40
41volatile static ElfW(auxv_t) *auxv = NULL;
42
43ElfW(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
86typedef unsigned int di_fpscr_t __attribute__ ((__mode__ (__DI__)));
87typedef 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
140void 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
185void 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
233void 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
284static void
285check_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
295int
296main (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

source code of glibc/sysdeps/powerpc/fpu/tst-setcontext-fpscr.c