~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/tm/tm-trap.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright 2017, Gustavo Romero, IBM Corp.
  4  *
  5  * Check if thread endianness is flipped inadvertently to BE on trap
  6  * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
  7  * load_fp and load_vec overflowed).
  8  *
  9  * The issue can be checked on LE machines simply by zeroing load_fp
 10  * and load_vec and then causing a trap in TM. Since the endianness
 11  * changes to BE on return from the signal handler, 'nop' is
 12  * thread as an illegal instruction in following sequence:
 13  *      tbegin.
 14  *      beq 1f
 15  *      trap
 16  *      tend.
 17  * 1:   nop
 18  *
 19  * However, although the issue is also present on BE machines, it's a
 20  * bit trickier to check it on BE machines because MSR.LE bit is set
 21  * to zero which determines a BE endianness that is the native
 22  * endianness on BE machines, so nothing notably critical happens,
 23  * i.e. no illegal instruction is observed immediately after returning
 24  * from the signal handler (as it happens on LE machines). Thus to test
 25  * it on BE machines LE endianness is forced after a first trap and then
 26  * the endianness is verified on subsequent traps to determine if the
 27  * endianness "flipped back" to the native endianness (BE).
 28  */
 29 
 30 #define _GNU_SOURCE
 31 #include <error.h>
 32 #include <stdio.h>
 33 #include <stdlib.h>
 34 #include <unistd.h>
 35 #include <htmintrin.h>
 36 #include <inttypes.h>
 37 #include <pthread.h>
 38 #include <sched.h>
 39 #include <signal.h>
 40 #include <stdbool.h>
 41 
 42 #include "tm.h"
 43 #include "utils.h"
 44 
 45 #define pr_error(error_code, format, ...) \
 46         error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__)
 47 
 48 #define MSR_LE 1UL
 49 #define LE     1UL
 50 
 51 pthread_t t0_ping;
 52 pthread_t t1_pong;
 53 
 54 int exit_from_pong;
 55 
 56 int trap_event;
 57 int le;
 58 
 59 bool success;
 60 
 61 void trap_signal_handler(int signo, siginfo_t *si, void *uc)
 62 {
 63         ucontext_t *ucp = uc;
 64         uint64_t thread_endianness;
 65 
 66         /* Get thread endianness: extract bit LE from MSR */
 67         thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
 68 
 69         /*
 70          * Little-Endian Machine
 71          */
 72 
 73         if (le) {
 74                 /* First trap event */
 75                 if (trap_event == 0) {
 76                         /* Do nothing. Since it is returning from this trap
 77                          * event that endianness is flipped by the bug, so just
 78                          * let the process return from the signal handler and
 79                          * check on the second trap event if endianness is
 80                          * flipped or not.
 81                          */
 82                 }
 83                 /* Second trap event */
 84                 else if (trap_event == 1) {
 85                         /*
 86                          * Since trap was caught in TM on first trap event, if
 87                          * endianness was still LE (not flipped inadvertently)
 88                          * after returning from the signal handler instruction
 89                          * (1) is executed (basically a 'nop'), as it's located
 90                          * at address of tbegin. +4 (rollback addr). As (1) on
 91                          * LE endianness does in effect nothing, instruction (2)
 92                          * is then executed again as 'trap', generating a second
 93                          * trap event (note that in that case 'trap' is caught
 94                          * not in transacional mode). On te other hand, if after
 95                          * the return from the signal handler the endianness in-
 96                          * advertently flipped, instruction (1) is tread as a
 97                          * branch instruction, i.e. b .+8, hence instruction (3)
 98                          * and (4) are executed (tbegin.; trap;) and we get sim-
 99                          * ilaly on the trap signal handler, but now in TM mode.
100                          * Either way, it's now possible to check the MSR LE bit
101                          * once in the trap handler to verify if endianness was
102                          * flipped or not after the return from the second trap
103                          * event. If endianness is flipped, the bug is present.
104                          * Finally, getting a trap in TM mode or not is just
105                          * worth noting because it affects the math to determine
106                          * the offset added to the NIP on return: the NIP for a
107                          * trap caught in TM is the rollback address, i.e. the
108                          * next instruction after 'tbegin.', whilst the NIP for
109                          * a trap caught in non-transactional mode is the very
110                          * same address of the 'trap' instruction that generated
111                          * the trap event.
112                          */
113 
114                         if (thread_endianness == LE) {
115                                 /* Go to 'success', i.e. instruction (6) */
116                                 ucp->uc_mcontext.gp_regs[PT_NIP] += 16;
117                         } else {
118                                 /*
119                                  * Thread endianness is BE, so it flipped
120                                  * inadvertently. Thus we flip back to LE and
121                                  * set NIP to go to 'failure', instruction (5).
122                                  */
123                                 ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
124                                 ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
125                         }
126                 }
127         }
128 
129         /*
130          * Big-Endian Machine
131          */
132 
133         else {
134                 /* First trap event */
135                 if (trap_event == 0) {
136                         /*
137                          * Force thread endianness to be LE. Instructions (1),
138                          * (3), and (4) will be executed, generating a second
139                          * trap in TM mode.
140                          */
141                         ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
142                 }
143                 /* Second trap event */
144                 else if (trap_event == 1) {
145                         /*
146                          * Do nothing. If bug is present on return from this
147                          * second trap event endianness will flip back "automat-
148                          * ically" to BE, otherwise thread endianness will
149                          * continue to be LE, just as it was set above.
150                          */
151                 }
152                 /* A third trap event */
153                 else {
154                         /*
155                          * Once here it means that after returning from the sec-
156                          * ond trap event instruction (4) (trap) was executed
157                          * as LE, generating a third trap event. In that case
158                          * endianness is still LE as set on return from the
159                          * first trap event, hence no bug. Otherwise, bug
160                          * flipped back to BE on return from the second trap
161                          * event and instruction (4) was executed as 'tdi' (so
162                          * basically a 'nop') and branch to 'failure' in
163                          * instruction (5) was taken to indicate failure and we
164                          * never get here.
165                          */
166 
167                         /*
168                          * Flip back to BE and go to instruction (6), i.e. go to
169                          * 'success'.
170                          */
171                         ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL;
172                         ucp->uc_mcontext.gp_regs[PT_NIP] += 8;
173                 }
174         }
175 
176         trap_event++;
177 }
178 
179 void usr1_signal_handler(int signo, siginfo_t *si, void *not_used)
180 {
181         /* Got a USR1 signal from ping(), so just tell pong() to exit */
182         exit_from_pong = 1;
183 }
184 
185 void *ping(void *not_used)
186 {
187         uint64_t i;
188 
189         trap_event = 0;
190 
191         /*
192          * Wait an amount of context switches so load_fp and load_vec overflows
193          * and MSR_[FP|VEC|V] is 0.
194          */
195         for (i = 0; i < 1024*1024*512; i++)
196                 ;
197 
198         asm goto(
199                 /*
200                  * [NA] means "Native Endianness", i.e. it tells how a
201                  * instruction is executed on machine's native endianness (in
202                  * other words, native endianness matches kernel endianness).
203                  * [OP] means "Opposite Endianness", i.e. on a BE machine, it
204                  * tells how a instruction is executed as a LE instruction; con-
205                  * versely, on a LE machine, it tells how a instruction is
206                  * executed as a BE instruction. When [NA] is omitted, it means
207                  * that the native interpretation of a given instruction is not
208                  * relevant for the test. Likewise when [OP] is omitted.
209                  */
210 
211                 " tbegin.        ;" /* (0) tbegin. [NA]                    */
212                 " tdi  0, 0, 0x48;" /* (1) nop     [NA]; b (3) [OP]        */
213                 " trap           ;" /* (2) trap    [NA]                    */
214                 ".long 0x1D05007C;" /* (3) tbegin. [OP]                    */
215                 ".long 0x0800E07F;" /* (4) trap    [OP]; nop   [NA]        */
216                 " b %l[failure]  ;" /* (5) b [NA]; MSR.LE flipped (bug)    */
217                 " b %l[success]  ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/
218 
219                 : : : : failure, success);
220 
221 failure:
222         success = false;
223         goto exit_from_ping;
224 
225 success:
226         success = true;
227 
228 exit_from_ping:
229         /* Tell pong() to exit before leaving */
230         pthread_kill(t1_pong, SIGUSR1);
231         return NULL;
232 }
233 
234 void *pong(void *not_used)
235 {
236         while (!exit_from_pong)
237                 /*
238                  * Induce context switches on ping() thread
239                  * until ping() finishes its job and signs
240                  * to exit from this loop.
241                  */
242                 sched_yield();
243 
244         return NULL;
245 }
246 
247 int tm_trap_test(void)
248 {
249         uint16_t k = 1;
250         int cpu, rc;
251 
252         pthread_attr_t attr;
253         cpu_set_t cpuset;
254 
255         struct sigaction trap_sa;
256 
257         SKIP_IF(!have_htm());
258         SKIP_IF(htm_is_synthetic());
259 
260         trap_sa.sa_flags = SA_SIGINFO;
261         trap_sa.sa_sigaction = trap_signal_handler;
262         sigaction(SIGTRAP, &trap_sa, NULL);
263 
264         struct sigaction usr1_sa;
265 
266         usr1_sa.sa_flags = SA_SIGINFO;
267         usr1_sa.sa_sigaction = usr1_signal_handler;
268         sigaction(SIGUSR1, &usr1_sa, NULL);
269 
270         cpu = pick_online_cpu();
271         FAIL_IF(cpu < 0);
272 
273         // Set only one CPU in the mask. Both threads will be bound to that CPU.
274         CPU_ZERO(&cpuset);
275         CPU_SET(cpu, &cpuset);
276 
277         /* Init pthread attribute */
278         rc = pthread_attr_init(&attr);
279         if (rc)
280                 pr_error(rc, "pthread_attr_init()");
281 
282         /*
283          * Bind thread ping() and pong() both to CPU 0 so they ping-pong and
284          * speed up context switches on ping() thread, speeding up the load_fp
285          * and load_vec overflow.
286          */
287         rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
288         if (rc)
289                 pr_error(rc, "pthread_attr_setaffinity()");
290 
291         /* Figure out the machine endianness */
292         le = (int) *(uint8_t *)&k;
293 
294         printf("%s machine detected. Checking if endianness flips %s",
295                 le ? "Little-Endian" : "Big-Endian",
296                 "inadvertently on trap in TM... ");
297 
298         rc = fflush(0);
299         if (rc)
300                 pr_error(rc, "fflush()");
301 
302         /* Launch ping() */
303         rc = pthread_create(&t0_ping, &attr, ping, NULL);
304         if (rc)
305                 pr_error(rc, "pthread_create()");
306 
307         exit_from_pong = 0;
308 
309         /* Launch pong() */
310         rc = pthread_create(&t1_pong, &attr, pong, NULL);
311         if (rc)
312                 pr_error(rc, "pthread_create()");
313 
314         rc = pthread_join(t0_ping, NULL);
315         if (rc)
316                 pr_error(rc, "pthread_join()");
317 
318         rc = pthread_join(t1_pong, NULL);
319         if (rc)
320                 pr_error(rc, "pthread_join()");
321 
322         if (success) {
323                 printf("no.\n"); /* no, endianness did not flip inadvertently */
324                 return EXIT_SUCCESS;
325         }
326 
327         printf("yes!\n"); /* yes, endianness did flip inadvertently */
328         return EXIT_FAILURE;
329 }
330 
331 int main(int argc, char **argv)
332 {
333         return test_harness(tm_trap_test, "tm_trap_test");
334 }
335 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php