162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2018, Breno Leitao, Gustavo Romero, IBM Corp. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This test raises a SIGUSR1 signal, and toggle the MSR[TS] 662306a36Sopenharmony_ci * fields at the signal handler. With MSR[TS] being set, the kernel will 762306a36Sopenharmony_ci * force a recheckpoint, which may cause a segfault when returning to 862306a36Sopenharmony_ci * user space. Since the test needs to re-run, the segfault needs to be 962306a36Sopenharmony_ci * caught and handled. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * In order to continue the test even after a segfault, the context is 1262306a36Sopenharmony_ci * saved prior to the signal being raised, and it is restored when there is 1362306a36Sopenharmony_ci * a segmentation fault. This happens for COUNT_MAX times. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * This test never fails (as returning EXIT_FAILURE). It either succeeds, 1662306a36Sopenharmony_ci * or crash the kernel (on a buggy kernel). 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define _GNU_SOURCE 2062306a36Sopenharmony_ci#include <stdio.h> 2162306a36Sopenharmony_ci#include <stdlib.h> 2262306a36Sopenharmony_ci#include <signal.h> 2362306a36Sopenharmony_ci#include <string.h> 2462306a36Sopenharmony_ci#include <ucontext.h> 2562306a36Sopenharmony_ci#include <unistd.h> 2662306a36Sopenharmony_ci#include <sys/mman.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "tm.h" 2962306a36Sopenharmony_ci#include "utils.h" 3062306a36Sopenharmony_ci#include "reg.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define COUNT_MAX 5000 /* Number of interactions */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * This test only runs on 64 bits system. Unsetting MSR_TS_S to avoid 3662306a36Sopenharmony_ci * compilation issue on 32 bits system. There is no side effect, since the 3762306a36Sopenharmony_ci * whole test will be skipped if it is not running on 64 bits system. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci#ifndef __powerpc64__ 4062306a36Sopenharmony_ci#undef MSR_TS_S 4162306a36Sopenharmony_ci#define MSR_TS_S 0 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Setting contexts because the test will crash and we want to recover */ 4562306a36Sopenharmony_ciucontext_t init_context; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* count is changed in the signal handler, so it must be volatile */ 4862306a36Sopenharmony_cistatic volatile int count; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_civoid usr_signal_handler(int signo, siginfo_t *si, void *uc) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci ucontext_t *ucp = uc; 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* 5662306a36Sopenharmony_ci * Allocating memory in a signal handler, and never freeing it on 5762306a36Sopenharmony_ci * purpose, forcing the heap increase, so, the memory leak is what 5862306a36Sopenharmony_ci * we want here. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci ucp->uc_link = mmap(NULL, sizeof(ucontext_t), 6162306a36Sopenharmony_ci PROT_READ | PROT_WRITE, 6262306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 6362306a36Sopenharmony_ci if (ucp->uc_link == (void *)-1) { 6462306a36Sopenharmony_ci perror("Mmap failed"); 6562306a36Sopenharmony_ci exit(-1); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Forcing the page to be allocated in a page fault */ 6962306a36Sopenharmony_ci ret = madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED); 7062306a36Sopenharmony_ci if (ret) { 7162306a36Sopenharmony_ci perror("madvise failed"); 7262306a36Sopenharmony_ci exit(-1); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci memcpy(&ucp->uc_link->uc_mcontext, &ucp->uc_mcontext, 7662306a36Sopenharmony_ci sizeof(ucp->uc_mcontext)); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* Forcing to enable MSR[TM] */ 7962306a36Sopenharmony_ci UCONTEXT_MSR(ucp) |= MSR_TS_S; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * A fork inside a signal handler seems to be more efficient than a 8362306a36Sopenharmony_ci * fork() prior to the signal being raised. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci if (fork() == 0) { 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Both child and parent will return, but, child returns 8862306a36Sopenharmony_ci * with count set so it will exit in the next segfault. 8962306a36Sopenharmony_ci * Parent will continue to loop. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci count = COUNT_MAX; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * If the change above does not hit the bug, it will cause a 9662306a36Sopenharmony_ci * segmentation fault, since the ck structures are NULL. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_civoid seg_signal_handler(int signo, siginfo_t *si, void *uc) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci count++; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Reexecute the test */ 10562306a36Sopenharmony_ci setcontext(&init_context); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid tm_trap_test(void) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct sigaction usr_sa, seg_sa; 11162306a36Sopenharmony_ci stack_t ss; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci usr_sa.sa_flags = SA_SIGINFO | SA_ONSTACK; 11462306a36Sopenharmony_ci usr_sa.sa_sigaction = usr_signal_handler; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci seg_sa.sa_flags = SA_SIGINFO; 11762306a36Sopenharmony_ci seg_sa.sa_sigaction = seg_signal_handler; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* 12062306a36Sopenharmony_ci * Set initial context. Will get back here from 12162306a36Sopenharmony_ci * seg_signal_handler() 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci getcontext(&init_context); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci while (count < COUNT_MAX) { 12662306a36Sopenharmony_ci /* Allocated an alternative signal stack area */ 12762306a36Sopenharmony_ci ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 12862306a36Sopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 12962306a36Sopenharmony_ci ss.ss_size = SIGSTKSZ; 13062306a36Sopenharmony_ci ss.ss_flags = 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (ss.ss_sp == (void *)-1) { 13362306a36Sopenharmony_ci perror("mmap error\n"); 13462306a36Sopenharmony_ci exit(-1); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Force the allocation through a page fault */ 13862306a36Sopenharmony_ci if (madvise(ss.ss_sp, SIGSTKSZ, MADV_DONTNEED)) { 13962306a36Sopenharmony_ci perror("madvise\n"); 14062306a36Sopenharmony_ci exit(-1); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Setting an alternative stack to generate a page fault when 14562306a36Sopenharmony_ci * the signal is raised. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (sigaltstack(&ss, NULL)) { 14862306a36Sopenharmony_ci perror("sigaltstack\n"); 14962306a36Sopenharmony_ci exit(-1); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* The signal handler will enable MSR_TS */ 15362306a36Sopenharmony_ci sigaction(SIGUSR1, &usr_sa, NULL); 15462306a36Sopenharmony_ci /* If it does not crash, it might segfault, avoid it to retest */ 15562306a36Sopenharmony_ci sigaction(SIGSEGV, &seg_sa, NULL); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci raise(SIGUSR1); 15862306a36Sopenharmony_ci count++; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint tm_signal_context_force_tm(void) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci SKIP_IF(!have_htm()); 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * Skipping if not running on 64 bits system, since I think it is 16762306a36Sopenharmony_ci * not possible to set mcontext's [MSR] with TS, due to it being 32 16862306a36Sopenharmony_ci * bits. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci SKIP_IF(!is_ppc64le()); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci tm_trap_test(); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return EXIT_SUCCESS; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint main(int argc, char **argv) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci test_harness(tm_signal_context_force_tm, "tm_signal_context_force_tm"); 18062306a36Sopenharmony_ci} 181