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