xref: /openbmc/qemu/tests/tcg/s390x/signals-s390x.c (revision 6882d651617cfc8ba3efd1722854d2143401e332)
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 
safe_puts(const char * s)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 
handle_signal(int sig,siginfo_t * info,void * ucontext)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 
check_sigsegv(void * func,enum exception exception,unsigned long val)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 
main_1(void)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