162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2017, Gustavo Romero, IBM Corp. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Check if thread endianness is flipped inadvertently to BE on trap 662306a36Sopenharmony_ci * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after 762306a36Sopenharmony_ci * load_fp and load_vec overflowed). 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * The issue can be checked on LE machines simply by zeroing load_fp 1062306a36Sopenharmony_ci * and load_vec and then causing a trap in TM. Since the endianness 1162306a36Sopenharmony_ci * changes to BE on return from the signal handler, 'nop' is 1262306a36Sopenharmony_ci * thread as an illegal instruction in following sequence: 1362306a36Sopenharmony_ci * tbegin. 1462306a36Sopenharmony_ci * beq 1f 1562306a36Sopenharmony_ci * trap 1662306a36Sopenharmony_ci * tend. 1762306a36Sopenharmony_ci * 1: nop 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * However, although the issue is also present on BE machines, it's a 2062306a36Sopenharmony_ci * bit trickier to check it on BE machines because MSR.LE bit is set 2162306a36Sopenharmony_ci * to zero which determines a BE endianness that is the native 2262306a36Sopenharmony_ci * endianness on BE machines, so nothing notably critical happens, 2362306a36Sopenharmony_ci * i.e. no illegal instruction is observed immediately after returning 2462306a36Sopenharmony_ci * from the signal handler (as it happens on LE machines). Thus to test 2562306a36Sopenharmony_ci * it on BE machines LE endianness is forced after a first trap and then 2662306a36Sopenharmony_ci * the endianness is verified on subsequent traps to determine if the 2762306a36Sopenharmony_ci * endianness "flipped back" to the native endianness (BE). 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define _GNU_SOURCE 3162306a36Sopenharmony_ci#include <error.h> 3262306a36Sopenharmony_ci#include <stdio.h> 3362306a36Sopenharmony_ci#include <stdlib.h> 3462306a36Sopenharmony_ci#include <unistd.h> 3562306a36Sopenharmony_ci#include <htmintrin.h> 3662306a36Sopenharmony_ci#include <inttypes.h> 3762306a36Sopenharmony_ci#include <pthread.h> 3862306a36Sopenharmony_ci#include <sched.h> 3962306a36Sopenharmony_ci#include <signal.h> 4062306a36Sopenharmony_ci#include <stdbool.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "tm.h" 4362306a36Sopenharmony_ci#include "utils.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define pr_error(error_code, format, ...) \ 4662306a36Sopenharmony_ci error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define MSR_LE 1UL 4962306a36Sopenharmony_ci#define LE 1UL 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cipthread_t t0_ping; 5262306a36Sopenharmony_cipthread_t t1_pong; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciint exit_from_pong; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciint trap_event; 5762306a36Sopenharmony_ciint le; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cibool success; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid trap_signal_handler(int signo, siginfo_t *si, void *uc) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci ucontext_t *ucp = uc; 6462306a36Sopenharmony_ci uint64_t thread_endianness; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Get thread endianness: extract bit LE from MSR */ 6762306a36Sopenharmony_ci thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR]; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Little-Endian Machine 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (le) { 7462306a36Sopenharmony_ci /* First trap event */ 7562306a36Sopenharmony_ci if (trap_event == 0) { 7662306a36Sopenharmony_ci /* Do nothing. Since it is returning from this trap 7762306a36Sopenharmony_ci * event that endianness is flipped by the bug, so just 7862306a36Sopenharmony_ci * let the process return from the signal handler and 7962306a36Sopenharmony_ci * check on the second trap event if endianness is 8062306a36Sopenharmony_ci * flipped or not. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci /* Second trap event */ 8462306a36Sopenharmony_ci else if (trap_event == 1) { 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Since trap was caught in TM on first trap event, if 8762306a36Sopenharmony_ci * endianness was still LE (not flipped inadvertently) 8862306a36Sopenharmony_ci * after returning from the signal handler instruction 8962306a36Sopenharmony_ci * (1) is executed (basically a 'nop'), as it's located 9062306a36Sopenharmony_ci * at address of tbegin. +4 (rollback addr). As (1) on 9162306a36Sopenharmony_ci * LE endianness does in effect nothing, instruction (2) 9262306a36Sopenharmony_ci * is then executed again as 'trap', generating a second 9362306a36Sopenharmony_ci * trap event (note that in that case 'trap' is caught 9462306a36Sopenharmony_ci * not in transacional mode). On te other hand, if after 9562306a36Sopenharmony_ci * the return from the signal handler the endianness in- 9662306a36Sopenharmony_ci * advertently flipped, instruction (1) is tread as a 9762306a36Sopenharmony_ci * branch instruction, i.e. b .+8, hence instruction (3) 9862306a36Sopenharmony_ci * and (4) are executed (tbegin.; trap;) and we get sim- 9962306a36Sopenharmony_ci * ilaly on the trap signal handler, but now in TM mode. 10062306a36Sopenharmony_ci * Either way, it's now possible to check the MSR LE bit 10162306a36Sopenharmony_ci * once in the trap handler to verify if endianness was 10262306a36Sopenharmony_ci * flipped or not after the return from the second trap 10362306a36Sopenharmony_ci * event. If endianness is flipped, the bug is present. 10462306a36Sopenharmony_ci * Finally, getting a trap in TM mode or not is just 10562306a36Sopenharmony_ci * worth noting because it affects the math to determine 10662306a36Sopenharmony_ci * the offset added to the NIP on return: the NIP for a 10762306a36Sopenharmony_ci * trap caught in TM is the rollback address, i.e. the 10862306a36Sopenharmony_ci * next instruction after 'tbegin.', whilst the NIP for 10962306a36Sopenharmony_ci * a trap caught in non-transactional mode is the very 11062306a36Sopenharmony_ci * same address of the 'trap' instruction that generated 11162306a36Sopenharmony_ci * the trap event. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (thread_endianness == LE) { 11562306a36Sopenharmony_ci /* Go to 'success', i.e. instruction (6) */ 11662306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_NIP] += 16; 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci /* 11962306a36Sopenharmony_ci * Thread endianness is BE, so it flipped 12062306a36Sopenharmony_ci * inadvertently. Thus we flip back to LE and 12162306a36Sopenharmony_ci * set NIP to go to 'failure', instruction (5). 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL; 12462306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_NIP] += 4; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Big-Endian Machine 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci else { 13462306a36Sopenharmony_ci /* First trap event */ 13562306a36Sopenharmony_ci if (trap_event == 0) { 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Force thread endianness to be LE. Instructions (1), 13862306a36Sopenharmony_ci * (3), and (4) will be executed, generating a second 13962306a36Sopenharmony_ci * trap in TM mode. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci /* Second trap event */ 14462306a36Sopenharmony_ci else if (trap_event == 1) { 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Do nothing. If bug is present on return from this 14762306a36Sopenharmony_ci * second trap event endianness will flip back "automat- 14862306a36Sopenharmony_ci * ically" to BE, otherwise thread endianness will 14962306a36Sopenharmony_ci * continue to be LE, just as it was set above. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci /* A third trap event */ 15362306a36Sopenharmony_ci else { 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * Once here it means that after returning from the sec- 15662306a36Sopenharmony_ci * ond trap event instruction (4) (trap) was executed 15762306a36Sopenharmony_ci * as LE, generating a third trap event. In that case 15862306a36Sopenharmony_ci * endianness is still LE as set on return from the 15962306a36Sopenharmony_ci * first trap event, hence no bug. Otherwise, bug 16062306a36Sopenharmony_ci * flipped back to BE on return from the second trap 16162306a36Sopenharmony_ci * event and instruction (4) was executed as 'tdi' (so 16262306a36Sopenharmony_ci * basically a 'nop') and branch to 'failure' in 16362306a36Sopenharmony_ci * instruction (5) was taken to indicate failure and we 16462306a36Sopenharmony_ci * never get here. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Flip back to BE and go to instruction (6), i.e. go to 16962306a36Sopenharmony_ci * 'success'. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL; 17262306a36Sopenharmony_ci ucp->uc_mcontext.gp_regs[PT_NIP] += 8; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci trap_event++; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_civoid usr1_signal_handler(int signo, siginfo_t *si, void *not_used) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci /* Got a USR1 signal from ping(), so just tell pong() to exit */ 18262306a36Sopenharmony_ci exit_from_pong = 1; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_civoid *ping(void *not_used) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci uint64_t i; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci trap_event = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Wait an amount of context switches so load_fp and load_vec overflows 19362306a36Sopenharmony_ci * and MSR_[FP|VEC|V] is 0. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci for (i = 0; i < 1024*1024*512; i++) 19662306a36Sopenharmony_ci ; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci asm goto( 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * [NA] means "Native Endianness", i.e. it tells how a 20162306a36Sopenharmony_ci * instruction is executed on machine's native endianness (in 20262306a36Sopenharmony_ci * other words, native endianness matches kernel endianness). 20362306a36Sopenharmony_ci * [OP] means "Opposite Endianness", i.e. on a BE machine, it 20462306a36Sopenharmony_ci * tells how a instruction is executed as a LE instruction; con- 20562306a36Sopenharmony_ci * versely, on a LE machine, it tells how a instruction is 20662306a36Sopenharmony_ci * executed as a BE instruction. When [NA] is omitted, it means 20762306a36Sopenharmony_ci * that the native interpretation of a given instruction is not 20862306a36Sopenharmony_ci * relevant for the test. Likewise when [OP] is omitted. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci " tbegin. ;" /* (0) tbegin. [NA] */ 21262306a36Sopenharmony_ci " tdi 0, 0, 0x48;" /* (1) nop [NA]; b (3) [OP] */ 21362306a36Sopenharmony_ci " trap ;" /* (2) trap [NA] */ 21462306a36Sopenharmony_ci ".long 0x1D05007C;" /* (3) tbegin. [OP] */ 21562306a36Sopenharmony_ci ".long 0x0800E07F;" /* (4) trap [OP]; nop [NA] */ 21662306a36Sopenharmony_ci " b %l[failure] ;" /* (5) b [NA]; MSR.LE flipped (bug) */ 21762306a36Sopenharmony_ci " b %l[success] ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci : : : : failure, success); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cifailure: 22262306a36Sopenharmony_ci success = false; 22362306a36Sopenharmony_ci goto exit_from_ping; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cisuccess: 22662306a36Sopenharmony_ci success = true; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ciexit_from_ping: 22962306a36Sopenharmony_ci /* Tell pong() to exit before leaving */ 23062306a36Sopenharmony_ci pthread_kill(t1_pong, SIGUSR1); 23162306a36Sopenharmony_ci return NULL; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid *pong(void *not_used) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci while (!exit_from_pong) 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Induce context switches on ping() thread 23962306a36Sopenharmony_ci * until ping() finishes its job and signs 24062306a36Sopenharmony_ci * to exit from this loop. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci sched_yield(); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return NULL; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint tm_trap_test(void) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci uint16_t k = 1; 25062306a36Sopenharmony_ci int cpu, rc; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pthread_attr_t attr; 25362306a36Sopenharmony_ci cpu_set_t cpuset; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci struct sigaction trap_sa; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci SKIP_IF(!have_htm()); 25862306a36Sopenharmony_ci SKIP_IF(htm_is_synthetic()); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci trap_sa.sa_flags = SA_SIGINFO; 26162306a36Sopenharmony_ci trap_sa.sa_sigaction = trap_signal_handler; 26262306a36Sopenharmony_ci sigaction(SIGTRAP, &trap_sa, NULL); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci struct sigaction usr1_sa; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci usr1_sa.sa_flags = SA_SIGINFO; 26762306a36Sopenharmony_ci usr1_sa.sa_sigaction = usr1_signal_handler; 26862306a36Sopenharmony_ci sigaction(SIGUSR1, &usr1_sa, NULL); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci cpu = pick_online_cpu(); 27162306a36Sopenharmony_ci FAIL_IF(cpu < 0); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci // Set only one CPU in the mask. Both threads will be bound to that CPU. 27462306a36Sopenharmony_ci CPU_ZERO(&cpuset); 27562306a36Sopenharmony_ci CPU_SET(cpu, &cpuset); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Init pthread attribute */ 27862306a36Sopenharmony_ci rc = pthread_attr_init(&attr); 27962306a36Sopenharmony_ci if (rc) 28062306a36Sopenharmony_ci pr_error(rc, "pthread_attr_init()"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Bind thread ping() and pong() both to CPU 0 so they ping-pong and 28462306a36Sopenharmony_ci * speed up context switches on ping() thread, speeding up the load_fp 28562306a36Sopenharmony_ci * and load_vec overflow. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); 28862306a36Sopenharmony_ci if (rc) 28962306a36Sopenharmony_ci pr_error(rc, "pthread_attr_setaffinity()"); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Figure out the machine endianness */ 29262306a36Sopenharmony_ci le = (int) *(uint8_t *)&k; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci printf("%s machine detected. Checking if endianness flips %s", 29562306a36Sopenharmony_ci le ? "Little-Endian" : "Big-Endian", 29662306a36Sopenharmony_ci "inadvertently on trap in TM... "); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci rc = fflush(0); 29962306a36Sopenharmony_ci if (rc) 30062306a36Sopenharmony_ci pr_error(rc, "fflush()"); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Launch ping() */ 30362306a36Sopenharmony_ci rc = pthread_create(&t0_ping, &attr, ping, NULL); 30462306a36Sopenharmony_ci if (rc) 30562306a36Sopenharmony_ci pr_error(rc, "pthread_create()"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci exit_from_pong = 0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Launch pong() */ 31062306a36Sopenharmony_ci rc = pthread_create(&t1_pong, &attr, pong, NULL); 31162306a36Sopenharmony_ci if (rc) 31262306a36Sopenharmony_ci pr_error(rc, "pthread_create()"); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci rc = pthread_join(t0_ping, NULL); 31562306a36Sopenharmony_ci if (rc) 31662306a36Sopenharmony_ci pr_error(rc, "pthread_join()"); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci rc = pthread_join(t1_pong, NULL); 31962306a36Sopenharmony_ci if (rc) 32062306a36Sopenharmony_ci pr_error(rc, "pthread_join()"); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (success) { 32362306a36Sopenharmony_ci printf("no.\n"); /* no, endianness did not flip inadvertently */ 32462306a36Sopenharmony_ci return EXIT_SUCCESS; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci printf("yes!\n"); /* yes, endianness did flip inadvertently */ 32862306a36Sopenharmony_ci return EXIT_FAILURE; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciint main(int argc, char **argv) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci return test_harness(tm_trap_test, "tm_trap_test"); 33462306a36Sopenharmony_ci} 335