Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2017, Gustavo Romero, IBM Corp.
0004  *
0005  * Check if thread endianness is flipped inadvertently to BE on trap
0006  * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
0007  * load_fp and load_vec overflowed).
0008  *
0009  * The issue can be checked on LE machines simply by zeroing load_fp
0010  * and load_vec and then causing a trap in TM. Since the endianness
0011  * changes to BE on return from the signal handler, 'nop' is
0012  * thread as an illegal instruction in following sequence:
0013  *  tbegin.
0014  *  beq 1f
0015  *  trap
0016  *  tend.
0017  * 1:   nop
0018  *
0019  * However, although the issue is also present on BE machines, it's a
0020  * bit trickier to check it on BE machines because MSR.LE bit is set
0021  * to zero which determines a BE endianness that is the native
0022  * endianness on BE machines, so nothing notably critical happens,
0023  * i.e. no illegal instruction is observed immediately after returning
0024  * from the signal handler (as it happens on LE machines). Thus to test
0025  * it on BE machines LE endianness is forced after a first trap and then
0026  * the endianness is verified on subsequent traps to determine if the
0027  * endianness "flipped back" to the native endianness (BE).
0028  */
0029 
0030 #define _GNU_SOURCE
0031 #include <error.h>
0032 #include <stdio.h>
0033 #include <stdlib.h>
0034 #include <unistd.h>
0035 #include <htmintrin.h>
0036 #include <inttypes.h>
0037 #include <pthread.h>
0038 #include <sched.h>
0039 #include <signal.h>
0040 #include <stdbool.h>
0041 
0042 #include "tm.h"
0043 #include "utils.h"
0044 
0045 #define pr_error(error_code, format, ...) \
0046     error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__)
0047 
0048 #define MSR_LE 1UL
0049 #define LE     1UL
0050 
0051 pthread_t t0_ping;
0052 pthread_t t1_pong;
0053 
0054 int exit_from_pong;
0055 
0056 int trap_event;
0057 int le;
0058 
0059 bool success;
0060 
0061 void trap_signal_handler(int signo, siginfo_t *si, void *uc)
0062 {
0063     ucontext_t *ucp = uc;
0064     uint64_t thread_endianness;
0065 
0066     /* Get thread endianness: extract bit LE from MSR */
0067     thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
0068 
0069     /*
0070      * Little-Endian Machine
0071      */
0072 
0073     if (le) {
0074         /* First trap event */
0075         if (trap_event == 0) {
0076             /* Do nothing. Since it is returning from this trap
0077              * event that endianness is flipped by the bug, so just
0078              * let the process return from the signal handler and
0079              * check on the second trap event if endianness is
0080              * flipped or not.
0081              */
0082         }
0083         /* Second trap event */
0084         else if (trap_event == 1) {
0085             /*
0086              * Since trap was caught in TM on first trap event, if
0087              * endianness was still LE (not flipped inadvertently)
0088              * after returning from the signal handler instruction
0089              * (1) is executed (basically a 'nop'), as it's located
0090              * at address of tbegin. +4 (rollback addr). As (1) on
0091              * LE endianness does in effect nothing, instruction (2)
0092              * is then executed again as 'trap', generating a second
0093              * trap event (note that in that case 'trap' is caught
0094              * not in transacional mode). On te other hand, if after
0095              * the return from the signal handler the endianness in-
0096              * advertently flipped, instruction (1) is tread as a
0097              * branch instruction, i.e. b .+8, hence instruction (3)
0098              * and (4) are executed (tbegin.; trap;) and we get sim-
0099              * ilaly on the trap signal handler, but now in TM mode.
0100              * Either way, it's now possible to check the MSR LE bit
0101              * once in the trap handler to verify if endianness was
0102              * flipped or not after the return from the second trap
0103              * event. If endianness is flipped, the bug is present.
0104              * Finally, getting a trap in TM mode or not is just
0105              * worth noting because it affects the math to determine
0106              * the offset added to the NIP on return: the NIP for a
0107              * trap caught in TM is the rollback address, i.e. the
0108              * next instruction after 'tbegin.', whilst the NIP for
0109              * a trap caught in non-transactional mode is the very
0110              * same address of the 'trap' instruction that generated
0111              * the trap event.
0112              */
0113 
0114             if (thread_endianness == LE) {
0115                 /* Go to 'success', i.e. instruction (6) */
0116                 ucp->uc_mcontext.gp_regs[PT_NIP] += 16;
0117             } else {
0118                 /*
0119                  * Thread endianness is BE, so it flipped
0120                  * inadvertently. Thus we flip back to LE and
0121                  * set NIP to go to 'failure', instruction (5).
0122                  */
0123                 ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
0124                 ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
0125             }
0126         }
0127     }
0128 
0129     /*
0130      * Big-Endian Machine
0131      */
0132 
0133     else {
0134         /* First trap event */
0135         if (trap_event == 0) {
0136             /*
0137              * Force thread endianness to be LE. Instructions (1),
0138              * (3), and (4) will be executed, generating a second
0139              * trap in TM mode.
0140              */
0141             ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
0142         }
0143         /* Second trap event */
0144         else if (trap_event == 1) {
0145             /*
0146              * Do nothing. If bug is present on return from this
0147              * second trap event endianness will flip back "automat-
0148              * ically" to BE, otherwise thread endianness will
0149              * continue to be LE, just as it was set above.
0150              */
0151         }
0152         /* A third trap event */
0153         else {
0154             /*
0155              * Once here it means that after returning from the sec-
0156              * ond trap event instruction (4) (trap) was executed
0157              * as LE, generating a third trap event. In that case
0158              * endianness is still LE as set on return from the
0159              * first trap event, hence no bug. Otherwise, bug
0160              * flipped back to BE on return from the second trap
0161              * event and instruction (4) was executed as 'tdi' (so
0162              * basically a 'nop') and branch to 'failure' in
0163              * instruction (5) was taken to indicate failure and we
0164              * never get here.
0165              */
0166 
0167             /*
0168              * Flip back to BE and go to instruction (6), i.e. go to
0169              * 'success'.
0170              */
0171             ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL;
0172             ucp->uc_mcontext.gp_regs[PT_NIP] += 8;
0173         }
0174     }
0175 
0176     trap_event++;
0177 }
0178 
0179 void usr1_signal_handler(int signo, siginfo_t *si, void *not_used)
0180 {
0181     /* Got a USR1 signal from ping(), so just tell pong() to exit */
0182     exit_from_pong = 1;
0183 }
0184 
0185 void *ping(void *not_used)
0186 {
0187     uint64_t i;
0188 
0189     trap_event = 0;
0190 
0191     /*
0192      * Wait an amount of context switches so load_fp and load_vec overflows
0193      * and MSR_[FP|VEC|V] is 0.
0194      */
0195     for (i = 0; i < 1024*1024*512; i++)
0196         ;
0197 
0198     asm goto(
0199         /*
0200          * [NA] means "Native Endianness", i.e. it tells how a
0201          * instruction is executed on machine's native endianness (in
0202          * other words, native endianness matches kernel endianness).
0203          * [OP] means "Opposite Endianness", i.e. on a BE machine, it
0204          * tells how a instruction is executed as a LE instruction; con-
0205          * versely, on a LE machine, it tells how a instruction is
0206          * executed as a BE instruction. When [NA] is omitted, it means
0207          * that the native interpretation of a given instruction is not
0208          * relevant for the test. Likewise when [OP] is omitted.
0209          */
0210 
0211         " tbegin.        ;" /* (0) tbegin. [NA]                    */
0212         " tdi  0, 0, 0x48;" /* (1) nop     [NA]; b (3) [OP]        */
0213         " trap           ;" /* (2) trap    [NA]                    */
0214         ".long 0x1D05007C;" /* (3) tbegin. [OP]                    */
0215         ".long 0x0800E07F;" /* (4) trap    [OP]; nop   [NA]        */
0216         " b %l[failure]  ;" /* (5) b [NA]; MSR.LE flipped (bug)    */
0217         " b %l[success]  ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/
0218 
0219         : : : : failure, success);
0220 
0221 failure:
0222     success = false;
0223     goto exit_from_ping;
0224 
0225 success:
0226     success = true;
0227 
0228 exit_from_ping:
0229     /* Tell pong() to exit before leaving */
0230     pthread_kill(t1_pong, SIGUSR1);
0231     return NULL;
0232 }
0233 
0234 void *pong(void *not_used)
0235 {
0236     while (!exit_from_pong)
0237         /*
0238          * Induce context switches on ping() thread
0239          * until ping() finishes its job and signs
0240          * to exit from this loop.
0241          */
0242         sched_yield();
0243 
0244     return NULL;
0245 }
0246 
0247 int tm_trap_test(void)
0248 {
0249     uint16_t k = 1;
0250     int cpu, rc;
0251 
0252     pthread_attr_t attr;
0253     cpu_set_t cpuset;
0254 
0255     struct sigaction trap_sa;
0256 
0257     SKIP_IF(!have_htm());
0258     SKIP_IF(htm_is_synthetic());
0259 
0260     trap_sa.sa_flags = SA_SIGINFO;
0261     trap_sa.sa_sigaction = trap_signal_handler;
0262     sigaction(SIGTRAP, &trap_sa, NULL);
0263 
0264     struct sigaction usr1_sa;
0265 
0266     usr1_sa.sa_flags = SA_SIGINFO;
0267     usr1_sa.sa_sigaction = usr1_signal_handler;
0268     sigaction(SIGUSR1, &usr1_sa, NULL);
0269 
0270     cpu = pick_online_cpu();
0271     FAIL_IF(cpu < 0);
0272 
0273     // Set only one CPU in the mask. Both threads will be bound to that CPU.
0274     CPU_ZERO(&cpuset);
0275     CPU_SET(cpu, &cpuset);
0276 
0277     /* Init pthread attribute */
0278     rc = pthread_attr_init(&attr);
0279     if (rc)
0280         pr_error(rc, "pthread_attr_init()");
0281 
0282     /*
0283      * Bind thread ping() and pong() both to CPU 0 so they ping-pong and
0284      * speed up context switches on ping() thread, speeding up the load_fp
0285      * and load_vec overflow.
0286      */
0287     rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
0288     if (rc)
0289         pr_error(rc, "pthread_attr_setaffinity()");
0290 
0291     /* Figure out the machine endianness */
0292     le = (int) *(uint8_t *)&k;
0293 
0294     printf("%s machine detected. Checking if endianness flips %s",
0295         le ? "Little-Endian" : "Big-Endian",
0296         "inadvertently on trap in TM... ");
0297 
0298     rc = fflush(0);
0299     if (rc)
0300         pr_error(rc, "fflush()");
0301 
0302     /* Launch ping() */
0303     rc = pthread_create(&t0_ping, &attr, ping, NULL);
0304     if (rc)
0305         pr_error(rc, "pthread_create()");
0306 
0307     exit_from_pong = 0;
0308 
0309     /* Launch pong() */
0310     rc = pthread_create(&t1_pong, &attr, pong, NULL);
0311     if (rc)
0312         pr_error(rc, "pthread_create()");
0313 
0314     rc = pthread_join(t0_ping, NULL);
0315     if (rc)
0316         pr_error(rc, "pthread_join()");
0317 
0318     rc = pthread_join(t1_pong, NULL);
0319     if (rc)
0320         pr_error(rc, "pthread_join()");
0321 
0322     if (success) {
0323         printf("no.\n"); /* no, endianness did not flip inadvertently */
0324         return EXIT_SUCCESS;
0325     }
0326 
0327     printf("yes!\n"); /* yes, endianness did flip inadvertently */
0328     return EXIT_FAILURE;
0329 }
0330 
0331 int main(int argc, char **argv)
0332 {
0333     return test_harness(tm_trap_test, "tm_trap_test");
0334 }