1 /* Check __ppc_get_hwcap() and __ppc_get_at_plaftorm() functionality.
2    Copyright (C) 2015-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* Tests if the hwcap, hwcap2 and platform data are stored in the TCB.  */
20 
21 #include <inttypes.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <pthread.h>
25 
26 #include <support/check.h>
27 #include <support/xthread.h>
28 
29 #include <sys/auxv.h>
30 
31 #include <dl-procinfo.h>
32 
33 #ifndef STATIC_TST_HWCAP
34 #undef PROCINFO_DECL
35 #include <dl-procinfo.c>
36 #endif
37 
38 /* Offsets copied from tcb-offsets.h.  */
39 
40 #ifdef __powerpc64__
41 # define __TPREG     "r13"
42 # define __HWCAPOFF -28776
43 # define __ATPLATOFF -28764
44 #else
45 # define __TPREG     "r2"
46 # define __HWCAPOFF -28736
47 # define __HWCAP2OFF -28732
48 # define __ATPLATOFF -28724
49 #endif
50 
check_tcbhwcap(long tid)51 uint64_t check_tcbhwcap (long tid)
52 {
53 
54   uint32_t tcb_at_platform, at_platform;
55   uint64_t hwcap, hwcap2, tcb_hwcap;
56   const char *at_platform_string;
57 
58   /* Testing if the hwcap/hwcap2 data is correctly initialized by
59      TLS_TP_INIT.  */
60 
61   register unsigned long __tp __asm__ (__TPREG);
62 
63 #ifdef __powerpc64__
64   __asm__  ("ld %0,%1(%2)\n"
65 	    : "=r" (tcb_hwcap)
66 	    : "n" (__HWCAPOFF), "b" (__tp));
67 #else
68   uint64_t h1, h2;
69 
70   __asm__ ("lwz %0,%1(%2)\n"
71       : "=r" (h1)
72       : "n" (__HWCAPOFF), "b" (__tp));
73   __asm__ ("lwz %0,%1(%2)\n"
74       : "=r" (h2)
75       : "n" (__HWCAP2OFF), "b" (__tp));
76   tcb_hwcap = (h1 >> 32) << 32 | (h2 >> 32);
77 #endif
78 
79   hwcap = getauxval (AT_HWCAP);
80   hwcap2 = getauxval (AT_HWCAP2);
81 
82   /* hwcap contains only the latest supported ISA, the code checks which is
83      and fills the previous supported ones.  This is necessary because the
84      same is done in hwcapinfo.c when setting the values that are copied to
85      the TCB.  */
86 
87   if (hwcap2 & PPC_FEATURE2_ARCH_2_07)
88     hwcap |= PPC_FEATURE_ARCH_2_06
89 	  | PPC_FEATURE_ARCH_2_05
90 	  | PPC_FEATURE_POWER5_PLUS
91 	  | PPC_FEATURE_POWER5
92 	  | PPC_FEATURE_POWER4;
93   else if (hwcap & PPC_FEATURE_ARCH_2_06)
94     hwcap |= PPC_FEATURE_ARCH_2_05
95 	  | PPC_FEATURE_POWER5_PLUS
96 	  | PPC_FEATURE_POWER5
97 	  | PPC_FEATURE_POWER4;
98   else if (hwcap & PPC_FEATURE_ARCH_2_05)
99     hwcap |= PPC_FEATURE_POWER5_PLUS
100 	  | PPC_FEATURE_POWER5
101 	  | PPC_FEATURE_POWER4;
102   else if (hwcap & PPC_FEATURE_POWER5_PLUS)
103     hwcap |= PPC_FEATURE_POWER5
104 	  | PPC_FEATURE_POWER4;
105   else if (hwcap & PPC_FEATURE_POWER5)
106     hwcap |= PPC_FEATURE_POWER4;
107 
108   hwcap = (hwcap << 32) + hwcap2;
109 
110   if ( tcb_hwcap != hwcap )
111     {
112       printf ("FAIL: __ppc_get_hwcap() - HWCAP is %" PRIx64 ". Should be %"
113 	      PRIx64 " for thread %ld.\n", tcb_hwcap, hwcap, tid);
114       return 1;
115     }
116 
117   /* Same test for the platform number.  */
118   __asm__  ("lwz %0,%1(%2)\n"
119 	    : "=r" (tcb_at_platform)
120 	    : "n" (__ATPLATOFF), "b" (__tp));
121 
122   at_platform_string = (const char *) getauxval (AT_PLATFORM);
123   at_platform = _dl_string_platform (at_platform_string);
124 
125   if ( tcb_at_platform != at_platform )
126     {
127       printf ("FAIL: __ppc_get_at_platform() - AT_PLATFORM is %x. Should be %x"
128 	     " for thread %ld\n", tcb_at_platform, at_platform, tid);
129       return 1;
130     }
131 
132   return 0;
133 }
134 
t1(void * tid)135 void *t1 (void *tid)
136 {
137   if (check_tcbhwcap ((long) tid))
138     {
139       pthread_exit (tid);
140     }
141 
142   pthread_exit (NULL);
143 
144 }
145 
146 static int
do_test(void)147 do_test (void)
148 {
149 
150   pthread_t threads[2];
151   pthread_attr_t attr;
152   pthread_attr_init (&attr);
153   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
154 
155   long i = 0;
156 
157   /* Check for main.  */
158   if (check_tcbhwcap (i))
159     {
160       return 1;
161     }
162 
163   /* Check for other thread.  */
164   i++;
165   threads[i] = xpthread_create (&attr, t1, (void *)i);
166 
167   pthread_attr_destroy (&attr);
168   TEST_VERIFY_EXIT (xpthread_join (threads[i]) == NULL);
169 
170   printf("PASS: HWCAP, HWCAP2 and AT_PLATFORM are correctly set in the TCB for"
171 	 " all threads.\n");
172 
173   pthread_exit (NULL);
174 
175 }
176 
177 #include <support/test-driver.c>
178