Message ID | 20210705210434.45824-3-iii@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | target/s390x: Fix SIGILL and SIGFPE psw.addr reporting | expand |
Ping. Could this patch be reviewed? The other code fix patch in this series has been reviewed so if these tests can be reviewed then both this series: https://lore.kernel.org/qemu-devel/20210705210434.45824-1-iii@linux.ibm.com/ and the dependent series: https://lore.kernel.org/qemu-devel/20210709160459.4962-1-jonathan.albrecht@linux.vnet.ibm.com/ will be reviewed and ready to go. Thanks, Jon On 2021-07-05 5:04 pm, Ilya Leoshkevich wrote: > Verify that s390x-specific uc_mcontext.psw.addr is reported correctly > and that signal handling interacts properly with debugging. > > Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> > --- > tests/tcg/s390x/Makefile.target | 18 +- > tests/tcg/s390x/gdbstub/test-signals-s390x.py | 76 ++++++++ > tests/tcg/s390x/signals-s390x.c | 165 ++++++++++++++++++ > 3 files changed, 258 insertions(+), 1 deletion(-) > create mode 100644 tests/tcg/s390x/gdbstub/test-signals-s390x.py > create mode 100644 tests/tcg/s390x/signals-s390x.c > > diff --git a/tests/tcg/s390x/Makefile.target > b/tests/tcg/s390x/Makefile.target > index 0036b8a505..0a5b25c156 100644 > --- a/tests/tcg/s390x/Makefile.target > +++ b/tests/tcg/s390x/Makefile.target > @@ -1,4 +1,5 @@ > -VPATH+=$(SRC_PATH)/tests/tcg/s390x > +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x > +VPATH+=$(S390X_SRC) > CFLAGS+=-march=zEC12 -m64 > TESTS+=hello-s390x > TESTS+=csst > @@ -12,3 +13,18 @@ TESTS+=mvc > # This triggers failures on s390x hosts about 4% of the time > run-signals: signals > $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups") > + > +TESTS+=signals-s390x > + > +ifneq ($(HAVE_GDB_BIN),) > +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py > + > +run-gdbstub-signals-s390x: signals-s390x > + $(call run-test, $@, $(GDB_SCRIPT) \ > + --gdb $(HAVE_GDB_BIN) \ > + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ > + --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ > + "mixing signals and debugging on s390x") > + > +EXTRA_RUNS += run-gdbstub-signals-s390x > +endif > diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py > b/tests/tcg/s390x/gdbstub/test-signals-s390x.py > new file mode 100644 > index 0000000000..80a284b475 > --- /dev/null > +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py > @@ -0,0 +1,76 @@ > +from __future__ import print_function > + > +# > +# Test that signals and debugging mix well together on s390x. > +# > +# This is launched via tests/guest-debug/run-test.py > +# > + > +import gdb > +import sys > + > +failcount = 0 > + > + > +def report(cond, msg): > + """Report success/fail of test""" > + if cond: > + print("PASS: %s" % (msg)) > + else: > + print("FAIL: %s" % (msg)) > + global failcount > + failcount += 1 > + > + > +def run_test(): > + """Run through the tests one by one""" > + illegal_op = gdb.Breakpoint("illegal_op") > + stg = gdb.Breakpoint("stg") > + mvc_8 = gdb.Breakpoint("mvc_8") > + > + # Expect the following events: > + # 1x illegal_op breakpoint > + # 2x stg breakpoint, segv, breakpoint > + # 2x mvc_8 breakpoint, segv, breakpoint > + for _ in range(14): > + gdb.execute("c") > + report(illegal_op.hit_count == 1, "illegal_op.hit_count == 1") > + report(stg.hit_count == 4, "stg.hit_count == 4") > + report(mvc_8.hit_count == 4, "mvc_8.hit_count == 4") > + > + # The test must succeed. > + gdb.Breakpoint("_exit") > + gdb.execute("c") > + status = int(gdb.parse_and_eval("$r2")) > + report(status == 0, "status == 0"); > + > + > +# > +# This runs as the script it sourced (via -x, via run-test.py) > +# > +try: > + inferior = gdb.selected_inferior() > + arch = inferior.architecture() > + print("ATTACHED: %s" % arch.name()) > +except (gdb.error, AttributeError): > + print("SKIPPING (not connected)", file=sys.stderr) > + exit(0) > + > +if gdb.parse_and_eval("$pc") == 0: > + print("SKIP: PC not set") > + exit(0) > + > +try: > + # These are not very useful in scripts > + gdb.execute("set pagination off") > + gdb.execute("set confirm off") > + > + # Run the actual tests > + run_test() > +except (gdb.error): > + print("GDB Exception: %s" % (sys.exc_info()[0])) > + failcount += 1 > + pass > + > +print("All tests complete: %d failures" % failcount) > +exit(failcount) > diff --git a/tests/tcg/s390x/signals-s390x.c > b/tests/tcg/s390x/signals-s390x.c > new file mode 100644 > index 0000000000..dc2f8ee59a > --- /dev/null > +++ b/tests/tcg/s390x/signals-s390x.c > @@ -0,0 +1,165 @@ > +#include <assert.h> > +#include <signal.h> > +#include <string.h> > +#include <sys/mman.h> > +#include <ucontext.h> > +#include <unistd.h> > + > +/* > + * Various instructions that generate SIGILL and SIGSEGV. They could > have been > + * defined in a separate .s file, but this would complicate the build, > so the > + * inline asm is used instead. > + */ > + > +void illegal_op(void); > +void after_illegal_op(void); > +asm(".globl\tillegal_op\n" > + "illegal_op:\t.byte\t0x00,0x00\n" > + "\t.globl\tafter_illegal_op\n" > + "after_illegal_op:\tbr\t%r14"); > + > +void stg(void *dst, unsigned long src); > +asm(".globl\tstg\n" > + "stg:\tstg\t%r3,0(%r2)\n" > + "\tbr\t%r14"); > + > +void mvc_8(void *dst, void *src); > +asm(".globl\tmvc_8\n" > + "mvc_8:\tmvc\t0(8,%r2),0(%r3)\n" > + "\tbr\t%r14"); > + > +static void safe_puts(const char *s) > +{ > + write(0, s, strlen(s)); > + write(0, "\n", 1); > +} > + > +enum exception { > + exception_operation, > + exception_translation, > + exception_protection, > +}; > + > +static struct { > + int sig; > + void *addr; > + unsigned long psw_addr; > + enum exception exception; > +} expected; > + > +static void handle_signal(int sig, siginfo_t *info, void *ucontext) > +{ > + void *page; > + int err; > + > + if (sig != expected.sig) { > + safe_puts("[ FAILED ] wrong signal"); > + _exit(1); > + } > + > + if (info->si_addr != expected.addr) { > + safe_puts("[ FAILED ] wrong si_addr"); > + _exit(1); > + } > + > + if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != > expected.psw_addr) { > + safe_puts("[ FAILED ] wrong psw.addr"); > + _exit(1); > + } > + > + switch (expected.exception) { > + case exception_translation: > + page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); > + if (page != expected.addr) { > + safe_puts("[ FAILED ] mmap() failed"); > + _exit(1); > + } > + break; > + case exception_protection: > + err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); > + if (err != 0) { > + safe_puts("[ FAILED ] mprotect() failed"); > + _exit(1); > + } > + break; > + default: > + break; > + } > +} > + > +static void check_sigsegv(void *func, enum exception exception, > + unsigned long val) > +{ > + int prot; > + unsigned long *page; > + unsigned long *addr; > + int err; > + > + prot = exception == exception_translation ? PROT_NONE : PROT_READ; > + page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); > + assert(page != MAP_FAILED); > + if (exception == exception_translation) { > + /* Hopefully nothing will be mapped at this address. */ > + err = munmap(page, 4096); > + assert(err == 0); > + } > + addr = page + (val & 0x1ff); > + > + expected.sig = SIGSEGV; > + expected.addr = page; > + expected.psw_addr = (unsigned long)func; > + expected.exception = exception; > + if (func == stg) { > + stg(addr, val); > + } else { > + assert(func == mvc_8); > + mvc_8(addr, &val); > + } > + assert(*addr == val); > + > + err = munmap(page, 4096); > + assert(err == 0); > +} > + > +int main(void) > +{ > + struct sigaction act; > + int err; > + > + memset(&act, 0, sizeof(act)); > + act.sa_sigaction = handle_signal; > + act.sa_flags = SA_SIGINFO; > + err = sigaction(SIGILL, &act, NULL); > + assert(err == 0); > + err = sigaction(SIGSEGV, &act, NULL); > + assert(err == 0); > + > + safe_puts("[ RUN ] Operation exception"); > + expected.sig = SIGILL; > + expected.addr = illegal_op; > + expected.psw_addr = (unsigned long)after_illegal_op; > + expected.exception = exception_operation; > + illegal_op(); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Translation exception from stg"); > + check_sigsegv(stg, exception_translation, 42); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Translation exception from mvc"); > + check_sigsegv(mvc_8, exception_translation, 4242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Protection exception from stg"); > + check_sigsegv(stg, exception_protection, 424242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Protection exception from mvc"); > + check_sigsegv(mvc_8, exception_protection, 42424242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ PASSED ]"); > + > + _exit(0); > +}
On Mon, 2021-07-05 at 23:04 +0200, Ilya Leoshkevich wrote: > Verify that s390x-specific uc_mcontext.psw.addr is reported correctly > and that signal handling interacts properly with debugging. > > Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> > --- > tests/tcg/s390x/Makefile.target | 18 +- > tests/tcg/s390x/gdbstub/test-signals-s390x.py | 76 ++++++++ > tests/tcg/s390x/signals-s390x.c | 165 > ++++++++++++++++++ > 3 files changed, 258 insertions(+), 1 deletion(-) > create mode 100644 tests/tcg/s390x/gdbstub/test-signals-s390x.py > create mode 100644 tests/tcg/s390x/signals-s390x.c > > diff --git a/tests/tcg/s390x/Makefile.target > b/tests/tcg/s390x/Makefile.target > index 0036b8a505..0a5b25c156 100644 > --- a/tests/tcg/s390x/Makefile.target > +++ b/tests/tcg/s390x/Makefile.target > @@ -1,4 +1,5 @@ > -VPATH+=$(SRC_PATH)/tests/tcg/s390x > +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x > +VPATH+=$(S390X_SRC) > CFLAGS+=-march=zEC12 -m64 > TESTS+=hello-s390x > TESTS+=csst > @@ -12,3 +13,18 @@ TESTS+=mvc > # This triggers failures on s390x hosts about 4% of the time > run-signals: signals > $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups") > + > +TESTS+=signals-s390x > + > +ifneq ($(HAVE_GDB_BIN),) > +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py > + > +run-gdbstub-signals-s390x: signals-s390x > + $(call run-test, $@, $(GDB_SCRIPT) \ > + --gdb $(HAVE_GDB_BIN) \ > + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ > + --bin $< --test $(S390X_SRC)/gdbstub/test-signals- > s390x.py, \ > + "mixing signals and debugging on s390x") > + > +EXTRA_RUNS += run-gdbstub-signals-s390x > +endif > diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py > b/tests/tcg/s390x/gdbstub/test-signals-s390x.py > new file mode 100644 > index 0000000000..80a284b475 > --- /dev/null > +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py > @@ -0,0 +1,76 @@ > +from __future__ import print_function > + > +# > +# Test that signals and debugging mix well together on s390x. > +# > +# This is launched via tests/guest-debug/run-test.py > +# > + > +import gdb > +import sys > + > +failcount = 0 > + > + > +def report(cond, msg): > + """Report success/fail of test""" > + if cond: > + print("PASS: %s" % (msg)) > + else: > + print("FAIL: %s" % (msg)) > + global failcount > + failcount += 1 > + > + > +def run_test(): > + """Run through the tests one by one""" > + illegal_op = gdb.Breakpoint("illegal_op") > + stg = gdb.Breakpoint("stg") > + mvc_8 = gdb.Breakpoint("mvc_8") > + > + # Expect the following events: > + # 1x illegal_op breakpoint > + # 2x stg breakpoint, segv, breakpoint > + # 2x mvc_8 breakpoint, segv, breakpoint > + for _ in range(14): > + gdb.execute("c") > + report(illegal_op.hit_count == 1, "illegal_op.hit_count == 1") > + report(stg.hit_count == 4, "stg.hit_count == 4") > + report(mvc_8.hit_count == 4, "mvc_8.hit_count == 4") > + > + # The test must succeed. > + gdb.Breakpoint("_exit") > + gdb.execute("c") > + status = int(gdb.parse_and_eval("$r2")) > + report(status == 0, "status == 0"); > + > + > +# > +# This runs as the script it sourced (via -x, via run-test.py) > +# > +try: > + inferior = gdb.selected_inferior() > + arch = inferior.architecture() > + print("ATTACHED: %s" % arch.name()) > +except (gdb.error, AttributeError): > + print("SKIPPING (not connected)", file=sys.stderr) > + exit(0) > + > +if gdb.parse_and_eval("$pc") == 0: > + print("SKIP: PC not set") > + exit(0) > + > +try: > + # These are not very useful in scripts > + gdb.execute("set pagination off") > + gdb.execute("set confirm off") > + > + # Run the actual tests > + run_test() > +except (gdb.error): > + print("GDB Exception: %s" % (sys.exc_info()[0])) > + failcount += 1 > + pass > + > +print("All tests complete: %d failures" % failcount) > +exit(failcount) > diff --git a/tests/tcg/s390x/signals-s390x.c > b/tests/tcg/s390x/signals-s390x.c > new file mode 100644 > index 0000000000..dc2f8ee59a > --- /dev/null > +++ b/tests/tcg/s390x/signals-s390x.c > @@ -0,0 +1,165 @@ > +#include <assert.h> > +#include <signal.h> > +#include <string.h> > +#include <sys/mman.h> > +#include <ucontext.h> > +#include <unistd.h> > + > +/* > + * Various instructions that generate SIGILL and SIGSEGV. They could > have been > + * defined in a separate .s file, but this would complicate the > build, so the > + * inline asm is used instead. > + */ > + > +void illegal_op(void); > +void after_illegal_op(void); > +asm(".globl\tillegal_op\n" > + "illegal_op:\t.byte\t0x00,0x00\n" > + "\t.globl\tafter_illegal_op\n" > + "after_illegal_op:\tbr\t%r14"); > + > +void stg(void *dst, unsigned long src); > +asm(".globl\tstg\n" > + "stg:\tstg\t%r3,0(%r2)\n" > + "\tbr\t%r14"); > + > +void mvc_8(void *dst, void *src); > +asm(".globl\tmvc_8\n" > + "mvc_8:\tmvc\t0(8,%r2),0(%r3)\n" > + "\tbr\t%r14"); > + > +static void safe_puts(const char *s) > +{ > + write(0, s, strlen(s)); > + write(0, "\n", 1); > +} > + > +enum exception { > + exception_operation, > + exception_translation, > + exception_protection, > +}; > + > +static struct { > + int sig; > + void *addr; > + unsigned long psw_addr; > + enum exception exception; > +} expected; > + > +static void handle_signal(int sig, siginfo_t *info, void *ucontext) > +{ > + void *page; > + int err; > + > + if (sig != expected.sig) { > + safe_puts("[ FAILED ] wrong signal"); > + _exit(1); > + } > + > + if (info->si_addr != expected.addr) { > + safe_puts("[ FAILED ] wrong si_addr"); > + _exit(1); > + } > + > + if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != > expected.psw_addr) { > + safe_puts("[ FAILED ] wrong psw.addr"); > + _exit(1); > + } > + > + switch (expected.exception) { > + case exception_translation: > + page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); > + if (page != expected.addr) { > + safe_puts("[ FAILED ] mmap() failed"); > + _exit(1); > + } > + break; > + case exception_protection: > + err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); > + if (err != 0) { > + safe_puts("[ FAILED ] mprotect() failed"); > + _exit(1); > + } > + break; > + default: > + break; > + } > +} > + > +static void check_sigsegv(void *func, enum exception exception, > + unsigned long val) > +{ > + int prot; > + unsigned long *page; > + unsigned long *addr; > + int err; > + > + prot = exception == exception_translation ? PROT_NONE : > PROT_READ; > + page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, > 0); > + assert(page != MAP_FAILED); > + if (exception == exception_translation) { > + /* Hopefully nothing will be mapped at this address. */ > + err = munmap(page, 4096); > + assert(err == 0); > + } > + addr = page + (val & 0x1ff); > + > + expected.sig = SIGSEGV; > + expected.addr = page; > + expected.psw_addr = (unsigned long)func; > + expected.exception = exception; > + if (func == stg) { > + stg(addr, val); > + } else { > + assert(func == mvc_8); > + mvc_8(addr, &val); > + } > + assert(*addr == val); > + > + err = munmap(page, 4096); > + assert(err == 0); > +} > + > +int main(void) > +{ > + struct sigaction act; > + int err; > + > + memset(&act, 0, sizeof(act)); > + act.sa_sigaction = handle_signal; > + act.sa_flags = SA_SIGINFO; > + err = sigaction(SIGILL, &act, NULL); > + assert(err == 0); > + err = sigaction(SIGSEGV, &act, NULL); > + assert(err == 0); > + > + safe_puts("[ RUN ] Operation exception"); > + expected.sig = SIGILL; > + expected.addr = illegal_op; > + expected.psw_addr = (unsigned long)after_illegal_op; > + expected.exception = exception_operation; > + illegal_op(); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Translation exception from stg"); > + check_sigsegv(stg, exception_translation, 42); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Translation exception from mvc"); > + check_sigsegv(mvc_8, exception_translation, 4242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Protection exception from stg"); > + check_sigsegv(stg, exception_protection, 424242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ RUN ] Protection exception from mvc"); > + check_sigsegv(mvc_8, exception_protection, 42424242); > + safe_puts("[ OK ]"); > + > + safe_puts("[ PASSED ]"); > + > + _exit(0); > +} Another ping - could somebody please have a look at this patch? Also, could the fix patch be picked separately? David has already reviewed it a while ago. Best regards, Ilya
On 05/07/2021 23.04, Ilya Leoshkevich wrote: > Verify that s390x-specific uc_mcontext.psw.addr is reported correctly > and that signal handling interacts properly with debugging. > > Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> > --- > tests/tcg/s390x/Makefile.target | 18 +- > tests/tcg/s390x/gdbstub/test-signals-s390x.py | 76 ++++++++ > tests/tcg/s390x/signals-s390x.c | 165 ++++++++++++++++++ > 3 files changed, 258 insertions(+), 1 deletion(-) > create mode 100644 tests/tcg/s390x/gdbstub/test-signals-s390x.py > create mode 100644 tests/tcg/s390x/signals-s390x.c > > diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target > index 0036b8a505..0a5b25c156 100644 > --- a/tests/tcg/s390x/Makefile.target > +++ b/tests/tcg/s390x/Makefile.target > @@ -1,4 +1,5 @@ > -VPATH+=$(SRC_PATH)/tests/tcg/s390x > +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x > +VPATH+=$(S390X_SRC) > CFLAGS+=-march=zEC12 -m64 > TESTS+=hello-s390x > TESTS+=csst > @@ -12,3 +13,18 @@ TESTS+=mvc > # This triggers failures on s390x hosts about 4% of the time > run-signals: signals > $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups") > + > +TESTS+=signals-s390x > + > +ifneq ($(HAVE_GDB_BIN),) > +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py > + > +run-gdbstub-signals-s390x: signals-s390x > + $(call run-test, $@, $(GDB_SCRIPT) \ > + --gdb $(HAVE_GDB_BIN) \ > + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ > + --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ > + "mixing signals and debugging on s390x") > + > +EXTRA_RUNS += run-gdbstub-signals-s390x > +endif Hi Ilya, FYI, I've put patch 1/2 into a pull request today, but this testing patch has a non-trivial conflict with commit eba61056e4cc ... could you please check how to get this enabled properly again and then respin this patch 2/2 here? Thanks! Thomas
diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 0036b8a505..0a5b25c156 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -1,4 +1,5 @@ -VPATH+=$(SRC_PATH)/tests/tcg/s390x +S390X_SRC=$(SRC_PATH)/tests/tcg/s390x +VPATH+=$(S390X_SRC) CFLAGS+=-march=zEC12 -m64 TESTS+=hello-s390x TESTS+=csst @@ -12,3 +13,18 @@ TESTS+=mvc # This triggers failures on s390x hosts about 4% of the time run-signals: signals $(call skip-test, $<, "BROKEN awaiting sigframe clean-ups") + +TESTS+=signals-s390x + +ifneq ($(HAVE_GDB_BIN),) +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py + +run-gdbstub-signals-s390x: signals-s390x + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(S390X_SRC)/gdbstub/test-signals-s390x.py, \ + "mixing signals and debugging on s390x") + +EXTRA_RUNS += run-gdbstub-signals-s390x +endif diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py b/tests/tcg/s390x/gdbstub/test-signals-s390x.py new file mode 100644 index 0000000000..80a284b475 --- /dev/null +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -0,0 +1,76 @@ +from __future__ import print_function + +# +# Test that signals and debugging mix well together on s390x. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +failcount = 0 + + +def report(cond, msg): + """Report success/fail of test""" + if cond: + print("PASS: %s" % (msg)) + else: + print("FAIL: %s" % (msg)) + global failcount + failcount += 1 + + +def run_test(): + """Run through the tests one by one""" + illegal_op = gdb.Breakpoint("illegal_op") + stg = gdb.Breakpoint("stg") + mvc_8 = gdb.Breakpoint("mvc_8") + + # Expect the following events: + # 1x illegal_op breakpoint + # 2x stg breakpoint, segv, breakpoint + # 2x mvc_8 breakpoint, segv, breakpoint + for _ in range(14): + gdb.execute("c") + report(illegal_op.hit_count == 1, "illegal_op.hit_count == 1") + report(stg.hit_count == 4, "stg.hit_count == 4") + report(mvc_8.hit_count == 4, "mvc_8.hit_count == 4") + + # The test must succeed. + gdb.Breakpoint("_exit") + gdb.execute("c") + status = int(gdb.parse_and_eval("$r2")) + report(status == 0, "status == 0"); + + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + arch = inferior.architecture() + print("ATTACHED: %s" % arch.name()) +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +if gdb.parse_and_eval("$pc") == 0: + print("SKIP: PC not set") + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except (gdb.error): + print("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + pass + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/s390x/signals-s390x.c b/tests/tcg/s390x/signals-s390x.c new file mode 100644 index 0000000000..dc2f8ee59a --- /dev/null +++ b/tests/tcg/s390x/signals-s390x.c @@ -0,0 +1,165 @@ +#include <assert.h> +#include <signal.h> +#include <string.h> +#include <sys/mman.h> +#include <ucontext.h> +#include <unistd.h> + +/* + * Various instructions that generate SIGILL and SIGSEGV. They could have been + * defined in a separate .s file, but this would complicate the build, so the + * inline asm is used instead. + */ + +void illegal_op(void); +void after_illegal_op(void); +asm(".globl\tillegal_op\n" + "illegal_op:\t.byte\t0x00,0x00\n" + "\t.globl\tafter_illegal_op\n" + "after_illegal_op:\tbr\t%r14"); + +void stg(void *dst, unsigned long src); +asm(".globl\tstg\n" + "stg:\tstg\t%r3,0(%r2)\n" + "\tbr\t%r14"); + +void mvc_8(void *dst, void *src); +asm(".globl\tmvc_8\n" + "mvc_8:\tmvc\t0(8,%r2),0(%r3)\n" + "\tbr\t%r14"); + +static void safe_puts(const char *s) +{ + write(0, s, strlen(s)); + write(0, "\n", 1); +} + +enum exception { + exception_operation, + exception_translation, + exception_protection, +}; + +static struct { + int sig; + void *addr; + unsigned long psw_addr; + enum exception exception; +} expected; + +static void handle_signal(int sig, siginfo_t *info, void *ucontext) +{ + void *page; + int err; + + if (sig != expected.sig) { + safe_puts("[ FAILED ] wrong signal"); + _exit(1); + } + + if (info->si_addr != expected.addr) { + safe_puts("[ FAILED ] wrong si_addr"); + _exit(1); + } + + if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) { + safe_puts("[ FAILED ] wrong psw.addr"); + _exit(1); + } + + switch (expected.exception) { + case exception_translation: + page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (page != expected.addr) { + safe_puts("[ FAILED ] mmap() failed"); + _exit(1); + } + break; + case exception_protection: + err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); + if (err != 0) { + safe_puts("[ FAILED ] mprotect() failed"); + _exit(1); + } + break; + default: + break; + } +} + +static void check_sigsegv(void *func, enum exception exception, + unsigned long val) +{ + int prot; + unsigned long *page; + unsigned long *addr; + int err; + + prot = exception == exception_translation ? PROT_NONE : PROT_READ; + page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(page != MAP_FAILED); + if (exception == exception_translation) { + /* Hopefully nothing will be mapped at this address. */ + err = munmap(page, 4096); + assert(err == 0); + } + addr = page + (val & 0x1ff); + + expected.sig = SIGSEGV; + expected.addr = page; + expected.psw_addr = (unsigned long)func; + expected.exception = exception; + if (func == stg) { + stg(addr, val); + } else { + assert(func == mvc_8); + mvc_8(addr, &val); + } + assert(*addr == val); + + err = munmap(page, 4096); + assert(err == 0); +} + +int main(void) +{ + struct sigaction act; + int err; + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_signal; + act.sa_flags = SA_SIGINFO; + err = sigaction(SIGILL, &act, NULL); + assert(err == 0); + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + safe_puts("[ RUN ] Operation exception"); + expected.sig = SIGILL; + expected.addr = illegal_op; + expected.psw_addr = (unsigned long)after_illegal_op; + expected.exception = exception_operation; + illegal_op(); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Translation exception from stg"); + check_sigsegv(stg, exception_translation, 42); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Translation exception from mvc"); + check_sigsegv(mvc_8, exception_translation, 4242); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Protection exception from stg"); + check_sigsegv(stg, exception_protection, 424242); + safe_puts("[ OK ]"); + + safe_puts("[ RUN ] Protection exception from mvc"); + check_sigsegv(mvc_8, exception_protection, 42424242); + safe_puts("[ OK ]"); + + safe_puts("[ PASSED ]"); + + _exit(0); +}
Verify that s390x-specific uc_mcontext.psw.addr is reported correctly and that signal handling interacts properly with debugging. Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com> --- tests/tcg/s390x/Makefile.target | 18 +- tests/tcg/s390x/gdbstub/test-signals-s390x.py | 76 ++++++++ tests/tcg/s390x/signals-s390x.c | 165 ++++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/s390x/gdbstub/test-signals-s390x.py create mode 100644 tests/tcg/s390x/signals-s390x.c