1e7f8a3aaSIlya Leoshkevich #include <assert.h> 2*1a75b140SIlya Leoshkevich #include <execinfo.h> 3e7f8a3aaSIlya Leoshkevich #include <signal.h> 4e7f8a3aaSIlya Leoshkevich #include <string.h> 5e7f8a3aaSIlya Leoshkevich #include <sys/mman.h> 6e7f8a3aaSIlya Leoshkevich #include <ucontext.h> 7e7f8a3aaSIlya Leoshkevich #include <unistd.h> 8e7f8a3aaSIlya Leoshkevich 9e7f8a3aaSIlya Leoshkevich /* 10e7f8a3aaSIlya Leoshkevich * Various instructions that generate SIGILL and SIGSEGV. They could have been 11e7f8a3aaSIlya Leoshkevich * defined in a separate .s file, but this would complicate the build, so the 12e7f8a3aaSIlya Leoshkevich * inline asm is used instead. 13e7f8a3aaSIlya Leoshkevich */ 14e7f8a3aaSIlya Leoshkevich 15*1a75b140SIlya Leoshkevich #define DEFINE_ASM_FUNCTION(name, body) \ 16*1a75b140SIlya Leoshkevich asm(".globl " #name "\n" \ 17*1a75b140SIlya Leoshkevich #name ":\n" \ 18*1a75b140SIlya Leoshkevich ".cfi_startproc\n" \ 19*1a75b140SIlya Leoshkevich body "\n" \ 20*1a75b140SIlya Leoshkevich "br %r14\n" \ 21*1a75b140SIlya Leoshkevich ".cfi_endproc"); 22*1a75b140SIlya Leoshkevich 23e7f8a3aaSIlya Leoshkevich void illegal_op(void); 24*1a75b140SIlya Leoshkevich extern const char after_illegal_op; 25*1a75b140SIlya Leoshkevich DEFINE_ASM_FUNCTION(illegal_op, 26*1a75b140SIlya Leoshkevich ".byte 0x00,0x00\n" 27*1a75b140SIlya Leoshkevich ".globl after_illegal_op\n" 28*1a75b140SIlya Leoshkevich "after_illegal_op:") 29e7f8a3aaSIlya Leoshkevich 30e7f8a3aaSIlya Leoshkevich void stg(void *dst, unsigned long src); 31*1a75b140SIlya Leoshkevich DEFINE_ASM_FUNCTION(stg, "stg %r3,0(%r2)") 32e7f8a3aaSIlya Leoshkevich 33e7f8a3aaSIlya Leoshkevich void mvc_8(void *dst, void *src); 34*1a75b140SIlya Leoshkevich DEFINE_ASM_FUNCTION(mvc_8, "mvc 0(8,%r2),0(%r3)") 35*1a75b140SIlya Leoshkevich 36*1a75b140SIlya Leoshkevich extern const char return_from_main_1; 37e7f8a3aaSIlya Leoshkevich 38e7f8a3aaSIlya Leoshkevich static void safe_puts(const char *s) 39e7f8a3aaSIlya Leoshkevich { 40e7f8a3aaSIlya Leoshkevich write(0, s, strlen(s)); 41e7f8a3aaSIlya Leoshkevich write(0, "\n", 1); 42e7f8a3aaSIlya Leoshkevich } 43e7f8a3aaSIlya Leoshkevich 44e7f8a3aaSIlya Leoshkevich enum exception { 45e7f8a3aaSIlya Leoshkevich exception_operation, 46e7f8a3aaSIlya Leoshkevich exception_translation, 47e7f8a3aaSIlya Leoshkevich exception_protection, 48e7f8a3aaSIlya Leoshkevich }; 49e7f8a3aaSIlya Leoshkevich 50e7f8a3aaSIlya Leoshkevich static struct { 51e7f8a3aaSIlya Leoshkevich int sig; 52e7f8a3aaSIlya Leoshkevich void *addr; 53e7f8a3aaSIlya Leoshkevich unsigned long psw_addr; 54e7f8a3aaSIlya Leoshkevich enum exception exception; 55e7f8a3aaSIlya Leoshkevich } expected; 56e7f8a3aaSIlya Leoshkevich 57e7f8a3aaSIlya Leoshkevich static void handle_signal(int sig, siginfo_t *info, void *ucontext) 58e7f8a3aaSIlya Leoshkevich { 59*1a75b140SIlya Leoshkevich int err, i, n_frames; 60*1a75b140SIlya Leoshkevich void *frames[16]; 61e7f8a3aaSIlya Leoshkevich void *page; 62e7f8a3aaSIlya Leoshkevich 63e7f8a3aaSIlya Leoshkevich if (sig != expected.sig) { 64e7f8a3aaSIlya Leoshkevich safe_puts("[ FAILED ] wrong signal"); 65e7f8a3aaSIlya Leoshkevich _exit(1); 66e7f8a3aaSIlya Leoshkevich } 67e7f8a3aaSIlya Leoshkevich 68e7f8a3aaSIlya Leoshkevich if (info->si_addr != expected.addr) { 69e7f8a3aaSIlya Leoshkevich safe_puts("[ FAILED ] wrong si_addr"); 70e7f8a3aaSIlya Leoshkevich _exit(1); 71e7f8a3aaSIlya Leoshkevich } 72e7f8a3aaSIlya Leoshkevich 73e7f8a3aaSIlya Leoshkevich if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) { 74e7f8a3aaSIlya Leoshkevich safe_puts("[ FAILED ] wrong psw.addr"); 75e7f8a3aaSIlya Leoshkevich _exit(1); 76e7f8a3aaSIlya Leoshkevich } 77e7f8a3aaSIlya Leoshkevich 78e7f8a3aaSIlya Leoshkevich switch (expected.exception) { 79e7f8a3aaSIlya Leoshkevich case exception_translation: 80e7f8a3aaSIlya Leoshkevich page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, 81e7f8a3aaSIlya Leoshkevich MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 82e7f8a3aaSIlya Leoshkevich if (page != expected.addr) { 83e7f8a3aaSIlya Leoshkevich safe_puts("[ FAILED ] mmap() failed"); 84e7f8a3aaSIlya Leoshkevich _exit(1); 85e7f8a3aaSIlya Leoshkevich } 86e7f8a3aaSIlya Leoshkevich break; 87e7f8a3aaSIlya Leoshkevich case exception_protection: 88e7f8a3aaSIlya Leoshkevich err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); 89e7f8a3aaSIlya Leoshkevich if (err != 0) { 90e7f8a3aaSIlya Leoshkevich safe_puts("[ FAILED ] mprotect() failed"); 91e7f8a3aaSIlya Leoshkevich _exit(1); 92e7f8a3aaSIlya Leoshkevich } 93e7f8a3aaSIlya Leoshkevich break; 94e7f8a3aaSIlya Leoshkevich default: 95e7f8a3aaSIlya Leoshkevich break; 96e7f8a3aaSIlya Leoshkevich } 97*1a75b140SIlya Leoshkevich 98*1a75b140SIlya Leoshkevich n_frames = backtrace(frames, sizeof(frames) / sizeof(frames[0])); 99*1a75b140SIlya Leoshkevich for (i = 0; i < n_frames; i++) { 100*1a75b140SIlya Leoshkevich if (frames[i] == &return_from_main_1) { 101*1a75b140SIlya Leoshkevich break; 102*1a75b140SIlya Leoshkevich } 103*1a75b140SIlya Leoshkevich } 104*1a75b140SIlya Leoshkevich if (i == n_frames) { 105*1a75b140SIlya Leoshkevich safe_puts("[ FAILED ] backtrace() is broken"); 106*1a75b140SIlya Leoshkevich _exit(1); 107*1a75b140SIlya Leoshkevich } 108e7f8a3aaSIlya Leoshkevich } 109e7f8a3aaSIlya Leoshkevich 110e7f8a3aaSIlya Leoshkevich static void check_sigsegv(void *func, enum exception exception, 111e7f8a3aaSIlya Leoshkevich unsigned long val) 112e7f8a3aaSIlya Leoshkevich { 113e7f8a3aaSIlya Leoshkevich int prot; 114e7f8a3aaSIlya Leoshkevich unsigned long *page; 115e7f8a3aaSIlya Leoshkevich unsigned long *addr; 116e7f8a3aaSIlya Leoshkevich int err; 117e7f8a3aaSIlya Leoshkevich 118e7f8a3aaSIlya Leoshkevich prot = exception == exception_translation ? PROT_NONE : PROT_READ; 119e7f8a3aaSIlya Leoshkevich page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 120e7f8a3aaSIlya Leoshkevich assert(page != MAP_FAILED); 121e7f8a3aaSIlya Leoshkevich if (exception == exception_translation) { 122e7f8a3aaSIlya Leoshkevich /* Hopefully nothing will be mapped at this address. */ 123e7f8a3aaSIlya Leoshkevich err = munmap(page, 4096); 124e7f8a3aaSIlya Leoshkevich assert(err == 0); 125e7f8a3aaSIlya Leoshkevich } 126e7f8a3aaSIlya Leoshkevich addr = page + (val & 0x1ff); 127e7f8a3aaSIlya Leoshkevich 128e7f8a3aaSIlya Leoshkevich expected.sig = SIGSEGV; 129e7f8a3aaSIlya Leoshkevich expected.addr = page; 130e7f8a3aaSIlya Leoshkevich expected.psw_addr = (unsigned long)func; 131e7f8a3aaSIlya Leoshkevich expected.exception = exception; 132e7f8a3aaSIlya Leoshkevich if (func == stg) { 133e7f8a3aaSIlya Leoshkevich stg(addr, val); 134e7f8a3aaSIlya Leoshkevich } else { 135e7f8a3aaSIlya Leoshkevich assert(func == mvc_8); 136e7f8a3aaSIlya Leoshkevich mvc_8(addr, &val); 137e7f8a3aaSIlya Leoshkevich } 138e7f8a3aaSIlya Leoshkevich assert(*addr == val); 139e7f8a3aaSIlya Leoshkevich 140e7f8a3aaSIlya Leoshkevich err = munmap(page, 4096); 141e7f8a3aaSIlya Leoshkevich assert(err == 0); 142e7f8a3aaSIlya Leoshkevich } 143e7f8a3aaSIlya Leoshkevich 144*1a75b140SIlya Leoshkevich int main_1(void) 145e7f8a3aaSIlya Leoshkevich { 146e7f8a3aaSIlya Leoshkevich struct sigaction act; 147e7f8a3aaSIlya Leoshkevich int err; 148e7f8a3aaSIlya Leoshkevich 149e7f8a3aaSIlya Leoshkevich memset(&act, 0, sizeof(act)); 150e7f8a3aaSIlya Leoshkevich act.sa_sigaction = handle_signal; 151e7f8a3aaSIlya Leoshkevich act.sa_flags = SA_SIGINFO; 152e7f8a3aaSIlya Leoshkevich err = sigaction(SIGILL, &act, NULL); 153e7f8a3aaSIlya Leoshkevich assert(err == 0); 154e7f8a3aaSIlya Leoshkevich err = sigaction(SIGSEGV, &act, NULL); 155e7f8a3aaSIlya Leoshkevich assert(err == 0); 156e7f8a3aaSIlya Leoshkevich 157e7f8a3aaSIlya Leoshkevich safe_puts("[ RUN ] Operation exception"); 158e7f8a3aaSIlya Leoshkevich expected.sig = SIGILL; 159e7f8a3aaSIlya Leoshkevich expected.addr = illegal_op; 160*1a75b140SIlya Leoshkevich expected.psw_addr = (unsigned long)&after_illegal_op; 161e7f8a3aaSIlya Leoshkevich expected.exception = exception_operation; 162e7f8a3aaSIlya Leoshkevich illegal_op(); 163e7f8a3aaSIlya Leoshkevich safe_puts("[ OK ]"); 164e7f8a3aaSIlya Leoshkevich 165e7f8a3aaSIlya Leoshkevich safe_puts("[ RUN ] Translation exception from stg"); 166e7f8a3aaSIlya Leoshkevich check_sigsegv(stg, exception_translation, 42); 167e7f8a3aaSIlya Leoshkevich safe_puts("[ OK ]"); 168e7f8a3aaSIlya Leoshkevich 169e7f8a3aaSIlya Leoshkevich safe_puts("[ RUN ] Translation exception from mvc"); 170e7f8a3aaSIlya Leoshkevich check_sigsegv(mvc_8, exception_translation, 4242); 171e7f8a3aaSIlya Leoshkevich safe_puts("[ OK ]"); 172e7f8a3aaSIlya Leoshkevich 173e7f8a3aaSIlya Leoshkevich safe_puts("[ RUN ] Protection exception from stg"); 174e7f8a3aaSIlya Leoshkevich check_sigsegv(stg, exception_protection, 424242); 175e7f8a3aaSIlya Leoshkevich safe_puts("[ OK ]"); 176e7f8a3aaSIlya Leoshkevich 177e7f8a3aaSIlya Leoshkevich safe_puts("[ RUN ] Protection exception from mvc"); 178e7f8a3aaSIlya Leoshkevich check_sigsegv(mvc_8, exception_protection, 42424242); 179e7f8a3aaSIlya Leoshkevich safe_puts("[ OK ]"); 180e7f8a3aaSIlya Leoshkevich 181e7f8a3aaSIlya Leoshkevich safe_puts("[ PASSED ]"); 182e7f8a3aaSIlya Leoshkevich 183e7f8a3aaSIlya Leoshkevich _exit(0); 184e7f8a3aaSIlya Leoshkevich } 185*1a75b140SIlya Leoshkevich 186*1a75b140SIlya Leoshkevich /* 187*1a75b140SIlya Leoshkevich * Define main() in assembly in order to test that unwinding from signal 188*1a75b140SIlya Leoshkevich * handlers until main() works. This way we can define a specific point that 189*1a75b140SIlya Leoshkevich * the unwinder should reach. This is also better than defining main() in C 190*1a75b140SIlya Leoshkevich * and using inline assembly to call main_1(), since it's not easy to get all 191*1a75b140SIlya Leoshkevich * the clobbers right. 192*1a75b140SIlya Leoshkevich */ 193*1a75b140SIlya Leoshkevich 194*1a75b140SIlya Leoshkevich DEFINE_ASM_FUNCTION(main, 195*1a75b140SIlya Leoshkevich "stmg %r14,%r15,112(%r15)\n" 196*1a75b140SIlya Leoshkevich ".cfi_offset 14,-48\n" 197*1a75b140SIlya Leoshkevich ".cfi_offset 15,-40\n" 198*1a75b140SIlya Leoshkevich "lay %r15,-160(%r15)\n" 199*1a75b140SIlya Leoshkevich ".cfi_def_cfa_offset 320\n" 200*1a75b140SIlya Leoshkevich "brasl %r14,main_1\n" 201*1a75b140SIlya Leoshkevich ".globl return_from_main_1\n" 202*1a75b140SIlya Leoshkevich "return_from_main_1:\n" 203*1a75b140SIlya Leoshkevich "lmg %r14,%r15,272(%r15)\n" 204*1a75b140SIlya Leoshkevich ".cfi_restore 15\n" 205*1a75b140SIlya Leoshkevich ".cfi_restore 14\n" 206*1a75b140SIlya Leoshkevich ".cfi_def_cfa_offset 160"); 207