xref: /openbmc/qemu/tests/tcg/s390x/signals-s390x.c (revision 1a75b140)
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 
23 void illegal_op(void);
24 extern const char after_illegal_op;
25 DEFINE_ASM_FUNCTION(illegal_op,
26     ".byte 0x00,0x00\n"
27     ".globl after_illegal_op\n"
28     "after_illegal_op:")
29 
30 void stg(void *dst, unsigned long src);
31 DEFINE_ASM_FUNCTION(stg, "stg %r3,0(%r2)")
32 
33 void mvc_8(void *dst, void *src);
34 DEFINE_ASM_FUNCTION(mvc_8, "mvc 0(8,%r2),0(%r3)")
35 
36 extern const char return_from_main_1;
37 
safe_puts(const char * s)38 static void safe_puts(const char *s)
39 {
40     write(0, s, strlen(s));
41     write(0, "\n", 1);
42 }
43 
44 enum exception {
45     exception_operation,
46     exception_translation,
47     exception_protection,
48 };
49 
50 static struct {
51     int sig;
52     void *addr;
53     unsigned long psw_addr;
54     enum exception exception;
55 } expected;
56 
handle_signal(int sig,siginfo_t * info,void * ucontext)57 static 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 
check_sigsegv(void * func,enum exception exception,unsigned long val)110 static 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 
main_1(void)144 int 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 
194 DEFINE_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