(root)/
glibc-2.38/
sysdeps/
powerpc/
fpu/
tst-setcontext-fpscr.c
       1  /* Copyright (C) 2001-2023 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("/proc/self/auxv", "r");
      56  
      57        if(auxv_f == 0)
      58  	{
      59  	  perror("Error opening file for reading");
      60  	  return 0;
      61  	}
      62        auxv = xmalloc (getpagesize ());
      63  
      64        do
      65  	{
      66  	  xfread (&auxv_struct, sizeof (ElfW(auxv_t)), 1, 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 ("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  }