tests/tcg/s390x: Test SIGILL and SIGSEGV handling
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> Acked-by: Alex Bennée <alex.bennee@linaro.org> Acked-by: David Hildenbrand <david@redhat.com> Message-Id: <20210804225146.154513-1-iii@linux.ibm.com> Signed-off-by: Cornelia Huck <cohuck@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
parent
89c6722da2
commit
e7f8a3aae2
@ -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
|
||||
@ -9,3 +10,17 @@ TESTS+=pack
|
||||
TESTS+=mvo
|
||||
TESTS+=mvc
|
||||
TESTS+=trap
|
||||
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
|
||||
|
76
tests/tcg/s390x/gdbstub/test-signals-s390x.py
Normal file
76
tests/tcg/s390x/gdbstub/test-signals-s390x.py
Normal file
@ -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)
|
165
tests/tcg/s390x/signals-s390x.c
Normal file
165
tests/tcg/s390x/signals-s390x.c
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user