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 | |
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 (format: "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 (str: at_platform_string); |
124 | |
125 | if ( tcb_at_platform != at_platform ) |
126 | { |
127 | printf (format: "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 | |
135 | void *t1 (void *tid) |
136 | { |
137 | if (check_tcbhwcap (tid: (long) tid)) |
138 | { |
139 | pthread_exit (retval: tid); |
140 | } |
141 | |
142 | pthread_exit (NULL); |
143 | |
144 | } |
145 | |
146 | static int |
147 | do_test (void) |
148 | { |
149 | |
150 | pthread_t threads[2]; |
151 | pthread_attr_t attr; |
152 | pthread_attr_init (attr: &attr); |
153 | pthread_attr_setdetachstate (attr: &attr, PTHREAD_CREATE_JOINABLE); |
154 | |
155 | long i = 0; |
156 | |
157 | /* Check for main. */ |
158 | if (check_tcbhwcap (tid: i)) |
159 | { |
160 | return 1; |
161 | } |
162 | |
163 | /* Check for other thread. */ |
164 | i++; |
165 | threads[i] = xpthread_create (attr: &attr, thread_func: t1, closure: (void *)i); |
166 | |
167 | pthread_attr_destroy (attr: &attr); |
168 | TEST_VERIFY_EXIT (xpthread_join (threads[i]) == NULL); |
169 | |
170 | printf(format: "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 | |