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