diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index f2d1215fb1..6a69a6dd26 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -25,6 +25,35 @@ /* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ #define S390X_FAIL_ADDR_MASK -4096LL +static int get_pgm_data_si_code(int dxc_code) +{ + switch (dxc_code) { + /* Non-simulated IEEE exceptions */ + case 0x80: + return TARGET_FPE_FLTINV; + case 0x40: + return TARGET_FPE_FLTDIV; + case 0x20: + case 0x28: + case 0x2c: + return TARGET_FPE_FLTOVF; + case 0x10: + case 0x18: + case 0x1c: + return TARGET_FPE_FLTUND; + case 0x08: + case 0x0c: + return TARGET_FPE_FLTRES; + } + /* + * Non-IEEE and simulated IEEE: + * Includes compare-and-trap, quantum exception, etc. + * Simulated IEEE are included here to match current + * s390x linux kernel. + */ + return 0; +} + void cpu_loop(CPUS390XState *env) { CPUState *cs = env_cpu(env); @@ -64,7 +93,13 @@ void cpu_loop(CPUS390XState *env) case EXCP_DEBUG: sig = TARGET_SIGTRAP; n = TARGET_TRAP_BRKPT; - goto do_signal_pc; + /* + * For SIGTRAP the PSW must point after the instruction, which it + * already does thanks to s390x_tr_tb_stop(). si_addr doesn't need + * to be filled. + */ + addr = 0; + goto do_signal; case EXCP_PGM: n = env->int_pgm_code; switch (n) { @@ -100,29 +135,14 @@ void cpu_loop(CPUS390XState *env) case PGM_DATA: n = (env->fpc >> 8) & 0xff; - if (n == 0xff) { - /* compare-and-trap */ + if (n == 0) { goto do_sigill_opn; - } else { - /* An IEEE exception, simulated or otherwise. */ - if (n & 0x80) { - n = TARGET_FPE_FLTINV; - } else if (n & 0x40) { - n = TARGET_FPE_FLTDIV; - } else if (n & 0x20) { - n = TARGET_FPE_FLTOVF; - } else if (n & 0x10) { - n = TARGET_FPE_FLTUND; - } else if (n & 0x08) { - n = TARGET_FPE_FLTRES; - } else { - /* ??? Quantum exception; BFP, DFP error. */ - goto do_sigill_opn; - } - sig = TARGET_SIGFPE; - goto do_signal_pc; } + sig = TARGET_SIGFPE; + n = get_pgm_data_si_code(n); + goto do_signal_pc; + default: fprintf(stderr, "Unhandled program exception: %#x\n", n); cpu_dump_state(cs, stderr, 0); @@ -132,6 +152,10 @@ void cpu_loop(CPUS390XState *env) do_signal_pc: addr = env->psw.addr; + /* + * For SIGILL and SIGFPE the PSW must point after the instruction. + */ + env->psw.addr += env->int_pgm_ilen; do_signal: info.si_signo = sig; info.si_errno = 0; diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 5d3de1b27a..bd084c7840 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -8,4 +8,4 @@ TESTS+=exrl-trtr TESTS+=pack TESTS+=mvo TESTS+=mvc - +TESTS+=trap diff --git a/tests/tcg/s390x/trap.c b/tests/tcg/s390x/trap.c new file mode 100644 index 0000000000..d4c61c7f52 --- /dev/null +++ b/tests/tcg/s390x/trap.c @@ -0,0 +1,102 @@ +/* + * Copyright 2021 IBM Corp. + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void error1(const char *filename, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", filename, line); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +static int __chk_error(const char *filename, int line, int ret) +{ + if (ret < 0) { + error1(filename, line, "%m (ret=%d, errno=%d/%s)", + ret, errno, strerror(errno)); + } + return ret; +} + +#define error(fmt, ...) error1(__FILE__, __LINE__, fmt, ## __VA_ARGS__) + +#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) + +int sigfpe_count; +int sigill_count; + +static void sig_handler(int sig, siginfo_t *si, void *puc) +{ + if (sig == SIGFPE) { + if (si->si_code != 0) { + error("unexpected si_code: 0x%x != 0", si->si_code); + } + ++sigfpe_count; + return; + } + + if (sig == SIGILL) { + ++sigill_count; + return; + } + + error("unexpected signal 0x%x\n", sig); +} + +int main(int argc, char **argv) +{ + sigfpe_count = sigill_count = 0; + + struct sigaction act; + + /* Set up SIG handler */ + act.sa_sigaction = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGFPE, &act, NULL)); + chk_error(sigaction(SIGILL, &act, NULL)); + + uint64_t z = 0x0ull; + uint64_t lz = 0xffffffffffffffffull; + asm volatile ( + "lg %%r13,%[lz]\n" + "cgitne %%r13,0\n" /* SIGFPE */ + "lg %%r13,%[z]\n" + "cgitne %%r13,0\n" /* no trap */ + "nopr\n" + "lg %%r13,%[lz]\n" + "citne %%r13,0\n" /* SIGFPE */ + "lg %%r13,%[z]\n" + "citne %%r13,0\n" /* no trap */ + "nopr\n" + : + : [z] "m" (z), [lz] "m" (lz) + : "memory", "r13"); + + if (sigfpe_count != 2) { + error("unexpected SIGFPE count: %d != 2", sigfpe_count); + } + if (sigill_count != 0) { + error("unexpected SIGILL count: %d != 0", sigill_count); + } + + printf("PASS\n"); + return 0; +}