1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Check that a fault signal handler gets the expected info
3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown */
4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <signal.h>
5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdio.h>
6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdlib.h>
7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <fcntl.h>
8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <setjmp.h>
9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "tests/sys_mman.h"
10ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <unistd.h>
11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/* Division by zero triggers a SIGFPE on x86 and x86_64,
13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   but not on the PowerPC architecture.
14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
15ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   On ARM-Linux, we do get a SIGFPE, but not from the faulting of a
16ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   division instruction (there isn't any such thing) but rather
17ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   because the process exits via tgkill, sending itself a SIGFPE.
18ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Hence we get a SIGFPE but the SI_CODE is different from that on
19ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   x86/amd64-linux.
20ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown */
21ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(__powerpc__)
22ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_TRIGGERS_FPE 0
23ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_SI_CODE      SI_TKILL
24ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#elif defined(__arm__)
25ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_TRIGGERS_FPE 1
26ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_SI_CODE      SI_TKILL
27ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
28ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_TRIGGERS_FPE 1
29ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  define DIVISION_BY_ZERO_SI_CODE      FPE_INTDIV
30ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
31ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
32ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
33ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstruct test {
34ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	void (*test)(void);
35ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	int sig;
36ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	int code;
37ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	volatile void *addr;
38ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown};
39ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
40ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic const struct test *cur_test;
41ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
42ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int zero();
43ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
44ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic jmp_buf escape;
45ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
46ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define BADADDR	((int *)0x1234)
47ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
48b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#define FILESIZE	(4*__pagesize)
49ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MAPSIZE		(2*FILESIZE)
50b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovstatic unsigned int __pagesize;
51ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic char volatile *volatile mapping;
52ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
53ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int testsig(int sig, int want)
54ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
55ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (sig != want) {
56ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		fprintf(stderr, "  FAIL: expected signal %d, not %d\n", want, sig);
57ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		return 0;
58ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	}
59ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	return 1;
60ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
61ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
62ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int testcode(int code, int want)
63ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
64ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (code != want) {
65ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		fprintf(stderr, "  FAIL: expected si_code==%d, not %d\n", want, code);
66ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		return 0;
67ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	}
68ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	return 1;
69ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
70ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
71ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int testaddr(void *addr, volatile void *want)
72ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
73b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov	/* Some architectures (e.g. s390) just provide enough information to
74b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         resolve the page fault, but do not provide the offset within a page */
75b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#if defined(__s390__)
76b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov	if (addr != (void *) ((unsigned long) want & ~0xffful)) {
77b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#else
78ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (addr != want) {
79b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#endif
80ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		fprintf(stderr, "  FAIL: expected si_addr==%p, not %p\n", want, addr);
81ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		return 0;
82ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	}
83ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	return 1;
84ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
85ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
86ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
87ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void handler(int sig, siginfo_t *si, void *uc)
88ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
89ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	int ok = 1;
90ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
91ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	ok = ok && testsig(sig, cur_test->sig);
92ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	ok = ok && testcode(si->si_code, cur_test->code);
93ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (cur_test->addr)
94ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		ok = ok && testaddr(si->si_addr, cur_test->addr);
95ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
96ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (ok)
97ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		fprintf(stderr, "  PASS\n");
98ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
99ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	siglongjmp(escape, ok + 1);
100ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
101ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void test1(void)
104ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	*BADADDR = 'x';
106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void test2()
109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
110ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	mapping[0] = 'x';
111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void test3()
114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	mapping[FILESIZE+10];
116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void test4()
119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	volatile int v = 44/zero();
121ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
122ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	(void)v;
123ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if DIVISION_BY_ZERO_TRIGGERS_FPE == 0
124ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	raise(SIGFPE);
125ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
126ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
127ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownint main()
129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
130ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	int fd, i;
131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	static const int sigs[] = { SIGSEGV, SIGILL, SIGBUS, SIGFPE, SIGTRAP };
132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	struct sigaction sa;
133b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov	__pagesize = (unsigned int)sysconf(_SC_PAGE_SIZE);
134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	sa.sa_sigaction = handler;
135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	sa.sa_flags = SA_SIGINFO;
136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	sigfillset(&sa.sa_mask);
137ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	for(i = 0; i < sizeof(sigs)/sizeof(*sigs); i++)
139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		sigaction(sigs[i], &sa, NULL);
140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
141b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov	/* we need O_RDWR for the truncate below */
142b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov	fd = open("faultstatus.tmp", O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600);
143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	if (fd == -1) {
144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		perror("tmpfile");
145ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		exit(1);
146ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	}
147ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	unlink("faultstatus.tmp");
148ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	ftruncate(fd, FILESIZE);
149ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
150ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	mapping = mmap(0, MAPSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
151ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	close(fd);
152ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
153ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	{
154ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		const struct test tests[] = {
155ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define T(n, sig, code, addr) { test##n, sig, code, addr }
156ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			T(1, SIGSEGV,	SEGV_MAPERR,	BADADDR),
157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			T(2, SIGSEGV,	SEGV_ACCERR,	mapping),
158ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			T(3, SIGBUS,	BUS_ADRERR,	&mapping[FILESIZE+10]),
159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			T(4, SIGFPE,    DIVISION_BY_ZERO_SI_CODE, 0),
160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#undef T
161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		};
162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) {
164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			cur_test = &tests[i];
165ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
166ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			if (sigsetjmp(escape, 1) == 0) {
167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown				fprintf(stderr, "Test %d: ", i+1);
168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown				tests[i].test();
169ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown				fprintf(stderr, "  FAIL: no fault, or handler returned\n");
170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown			}
171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown		}
172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	}
173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	return 0;
175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int zero()
178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
179ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown	return 0;
180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
181