123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright 2017, Gustavo Romero, IBM Corp.
- *
- * Check if thread endianness is flipped inadvertently to BE on trap
- * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after
- * load_fp and load_vec overflowed).
- *
- * The issue can be checked on LE machines simply by zeroing load_fp
- * and load_vec and then causing a trap in TM. Since the endianness
- * changes to BE on return from the signal handler, 'nop' is
- * thread as an illegal instruction in following sequence:
- * tbegin.
- * beq 1f
- * trap
- * tend.
- * 1: nop
- *
- * However, although the issue is also present on BE machines, it's a
- * bit trickier to check it on BE machines because MSR.LE bit is set
- * to zero which determines a BE endianness that is the native
- * endianness on BE machines, so nothing notably critical happens,
- * i.e. no illegal instruction is observed immediately after returning
- * from the signal handler (as it happens on LE machines). Thus to test
- * it on BE machines LE endianness is forced after a first trap and then
- * the endianness is verified on subsequent traps to determine if the
- * endianness "flipped back" to the native endianness (BE).
- */
- #define _GNU_SOURCE
- #include <error.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <htmintrin.h>
- #include <inttypes.h>
- #include <pthread.h>
- #include <sched.h>
- #include <signal.h>
- #include <stdbool.h>
- #include "tm.h"
- #include "utils.h"
- #define pr_error(error_code, format, ...) \
- error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__)
- #define MSR_LE 1UL
- #define LE 1UL
- pthread_t t0_ping;
- pthread_t t1_pong;
- int exit_from_pong;
- int trap_event;
- int le;
- bool success;
- void trap_signal_handler(int signo, siginfo_t *si, void *uc)
- {
- ucontext_t *ucp = uc;
- uint64_t thread_endianness;
- /* Get thread endianness: extract bit LE from MSR */
- thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR];
- /*
- * Little-Endian Machine
- */
- if (le) {
- /* First trap event */
- if (trap_event == 0) {
- /* Do nothing. Since it is returning from this trap
- * event that endianness is flipped by the bug, so just
- * let the process return from the signal handler and
- * check on the second trap event if endianness is
- * flipped or not.
- */
- }
- /* Second trap event */
- else if (trap_event == 1) {
- /*
- * Since trap was caught in TM on first trap event, if
- * endianness was still LE (not flipped inadvertently)
- * after returning from the signal handler instruction
- * (1) is executed (basically a 'nop'), as it's located
- * at address of tbegin. +4 (rollback addr). As (1) on
- * LE endianness does in effect nothing, instruction (2)
- * is then executed again as 'trap', generating a second
- * trap event (note that in that case 'trap' is caught
- * not in transacional mode). On te other hand, if after
- * the return from the signal handler the endianness in-
- * advertently flipped, instruction (1) is tread as a
- * branch instruction, i.e. b .+8, hence instruction (3)
- * and (4) are executed (tbegin.; trap;) and we get sim-
- * ilaly on the trap signal handler, but now in TM mode.
- * Either way, it's now possible to check the MSR LE bit
- * once in the trap handler to verify if endianness was
- * flipped or not after the return from the second trap
- * event. If endianness is flipped, the bug is present.
- * Finally, getting a trap in TM mode or not is just
- * worth noting because it affects the math to determine
- * the offset added to the NIP on return: the NIP for a
- * trap caught in TM is the rollback address, i.e. the
- * next instruction after 'tbegin.', whilst the NIP for
- * a trap caught in non-transactional mode is the very
- * same address of the 'trap' instruction that generated
- * the trap event.
- */
- if (thread_endianness == LE) {
- /* Go to 'success', i.e. instruction (6) */
- ucp->uc_mcontext.gp_regs[PT_NIP] += 16;
- } else {
- /*
- * Thread endianness is BE, so it flipped
- * inadvertently. Thus we flip back to LE and
- * set NIP to go to 'failure', instruction (5).
- */
- ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
- ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
- }
- }
- }
- /*
- * Big-Endian Machine
- */
- else {
- /* First trap event */
- if (trap_event == 0) {
- /*
- * Force thread endianness to be LE. Instructions (1),
- * (3), and (4) will be executed, generating a second
- * trap in TM mode.
- */
- ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL;
- }
- /* Second trap event */
- else if (trap_event == 1) {
- /*
- * Do nothing. If bug is present on return from this
- * second trap event endianness will flip back "automat-
- * ically" to BE, otherwise thread endianness will
- * continue to be LE, just as it was set above.
- */
- }
- /* A third trap event */
- else {
- /*
- * Once here it means that after returning from the sec-
- * ond trap event instruction (4) (trap) was executed
- * as LE, generating a third trap event. In that case
- * endianness is still LE as set on return from the
- * first trap event, hence no bug. Otherwise, bug
- * flipped back to BE on return from the second trap
- * event and instruction (4) was executed as 'tdi' (so
- * basically a 'nop') and branch to 'failure' in
- * instruction (5) was taken to indicate failure and we
- * never get here.
- */
- /*
- * Flip back to BE and go to instruction (6), i.e. go to
- * 'success'.
- */
- ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL;
- ucp->uc_mcontext.gp_regs[PT_NIP] += 8;
- }
- }
- trap_event++;
- }
- void usr1_signal_handler(int signo, siginfo_t *si, void *not_used)
- {
- /* Got a USR1 signal from ping(), so just tell pong() to exit */
- exit_from_pong = 1;
- }
- void *ping(void *not_used)
- {
- uint64_t i;
- trap_event = 0;
- /*
- * Wait an amount of context switches so load_fp and load_vec overflows
- * and MSR_[FP|VEC|V] is 0.
- */
- for (i = 0; i < 1024*1024*512; i++)
- ;
- asm goto(
- /*
- * [NA] means "Native Endianness", i.e. it tells how a
- * instruction is executed on machine's native endianness (in
- * other words, native endianness matches kernel endianness).
- * [OP] means "Opposite Endianness", i.e. on a BE machine, it
- * tells how a instruction is executed as a LE instruction; con-
- * versely, on a LE machine, it tells how a instruction is
- * executed as a BE instruction. When [NA] is omitted, it means
- * that the native interpretation of a given instruction is not
- * relevant for the test. Likewise when [OP] is omitted.
- */
- " tbegin. ;" /* (0) tbegin. [NA] */
- " tdi 0, 0, 0x48;" /* (1) nop [NA]; b (3) [OP] */
- " trap ;" /* (2) trap [NA] */
- ".long 0x1D05007C;" /* (3) tbegin. [OP] */
- ".long 0x0800E07F;" /* (4) trap [OP]; nop [NA] */
- " b %l[failure] ;" /* (5) b [NA]; MSR.LE flipped (bug) */
- " b %l[success] ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/
- : : : : failure, success);
- failure:
- success = false;
- goto exit_from_ping;
- success:
- success = true;
- exit_from_ping:
- /* Tell pong() to exit before leaving */
- pthread_kill(t1_pong, SIGUSR1);
- return NULL;
- }
- void *pong(void *not_used)
- {
- while (!exit_from_pong)
- /*
- * Induce context switches on ping() thread
- * until ping() finishes its job and signs
- * to exit from this loop.
- */
- sched_yield();
- return NULL;
- }
- int tm_trap_test(void)
- {
- uint16_t k = 1;
- int cpu, rc;
- pthread_attr_t attr;
- cpu_set_t cpuset;
- struct sigaction trap_sa;
- SKIP_IF(!have_htm());
- SKIP_IF(htm_is_synthetic());
- trap_sa.sa_flags = SA_SIGINFO;
- trap_sa.sa_sigaction = trap_signal_handler;
- sigaction(SIGTRAP, &trap_sa, NULL);
- struct sigaction usr1_sa;
- usr1_sa.sa_flags = SA_SIGINFO;
- usr1_sa.sa_sigaction = usr1_signal_handler;
- sigaction(SIGUSR1, &usr1_sa, NULL);
- cpu = pick_online_cpu();
- FAIL_IF(cpu < 0);
- // Set only one CPU in the mask. Both threads will be bound to that CPU.
- CPU_ZERO(&cpuset);
- CPU_SET(cpu, &cpuset);
- /* Init pthread attribute */
- rc = pthread_attr_init(&attr);
- if (rc)
- pr_error(rc, "pthread_attr_init()");
- /*
- * Bind thread ping() and pong() both to CPU 0 so they ping-pong and
- * speed up context switches on ping() thread, speeding up the load_fp
- * and load_vec overflow.
- */
- rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
- if (rc)
- pr_error(rc, "pthread_attr_setaffinity()");
- /* Figure out the machine endianness */
- le = (int) *(uint8_t *)&k;
- printf("%s machine detected. Checking if endianness flips %s",
- le ? "Little-Endian" : "Big-Endian",
- "inadvertently on trap in TM... ");
- rc = fflush(0);
- if (rc)
- pr_error(rc, "fflush()");
- /* Launch ping() */
- rc = pthread_create(&t0_ping, &attr, ping, NULL);
- if (rc)
- pr_error(rc, "pthread_create()");
- exit_from_pong = 0;
- /* Launch pong() */
- rc = pthread_create(&t1_pong, &attr, pong, NULL);
- if (rc)
- pr_error(rc, "pthread_create()");
- rc = pthread_join(t0_ping, NULL);
- if (rc)
- pr_error(rc, "pthread_join()");
- rc = pthread_join(t1_pong, NULL);
- if (rc)
- pr_error(rc, "pthread_join()");
- if (success) {
- printf("no.\n"); /* no, endianness did not flip inadvertently */
- return EXIT_SUCCESS;
- }
- printf("yes!\n"); /* yes, endianness did flip inadvertently */
- return EXIT_FAILURE;
- }
- int main(int argc, char **argv)
- {
- return test_harness(tm_trap_test, "tm_trap_test");
- }
|