1 /*
2  * Copyright (c) 2009 Cisco Systems, Inc.  All Rights Reserved.
3  * Copyright (c) 2009 FUJITSU LIMITED.  All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * Further, this software is distributed without any warranty that it is
14  * free of the rightful claim of any third person regarding infringement
15  * or the like.  Any license provided herein, whether implied or
16  * otherwise, applies only to this software file.  Patent licenses, if
17  * any, provided herein do not apply to combinations of this program with
18  * other software, or any other product whatsoever.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  *
24  * Author: Liu Bo <liubo2009@cn.fujitsu.com>
25  * Author: Ngie Cooper <yaneurabeya@gmail.com>
26  *
27  */
28 
29 #ifndef LTP_RT_SIGACTION_H
30 #define LTP_RT_SIGACTION_H
31 
32 #include "ltp_signal.h"
33 
34 #define INVAL_SA_PTR ((void *)-1)
35 
36 #if defined(__mips__)
37 struct kernel_sigaction {
38 	unsigned int sa_flags;
39 	void (* k_sa_handler)(int);
40 	sigset_t sa_mask;
41 };
42 #else
43 struct kernel_sigaction {
44 	void (* k_sa_handler)(int);
45 	unsigned long sa_flags;
46 	void (*sa_restorer) (void);
47 	sigset_t sa_mask;
48 };
49 #endif
50 
51 /* This macro marks if (struct sigaction) has .sa_restorer member */
52 #if !defined(__ia64__) && !defined(__alpha__) && !defined(__hppa__) && !defined(__mips__)
53 # define HAVE_SA_RESTORER
54 #endif
55 
56 #ifdef __x86_64__
57 
58 /*
59  * From asm/signal.h -- this value isn't exported anywhere outside of glibc and
60  * asm/signal.h and is only required for the rt_sig* function family because
61  * sigaction(2), et all, appends this if necessary to
62  * (struct sigaction).sa_flags. HEH.
63  *
64  * I do #undef though, just in case...
65  *
66  * Also, from .../arch/x86/kernel/signal.c:448 for v2.6.30 (something or
67  * other):
68  *
69  * x86-64 should always use SA_RESTORER.
70  *
71  * -- thus SA_RESTORER must always be defined along with
72  * (struct sigaction).sa_restorer for this architecture.
73  */
74 #undef SA_RESTORER
75 #define SA_RESTORER     0x04000000
76 
77 void (*restore_rt)(void);
78 
handler_h(int signal)79 static void handler_h(int signal)
80 {
81 	return;
82 }
83 
84 /* Setup an initial signal handler for signal number = sig for x86_64. */
sig_initial(int sig)85 static inline int sig_initial(int sig)
86 {
87 	int ret_code = -1;
88 	struct sigaction act, oact;
89 
90 	act.sa_handler = handler_h;
91 	act.sa_flags = 0;
92 	/* Clear out the signal set. */
93 	if (sigemptyset(&act.sa_mask) < 0) {
94 		/* Add the signal to the mask set. */
95 	} else if (sigaddset(&act.sa_mask, sig) < 0) {
96 		/* Set act.sa_restorer via syscall(2) */
97 	} else if (sigaction(sig, &act, &oact) < 0) {
98 		/* Copy oact.sa_restorer via syscall(2) */
99 	} else if (sigaction(sig, &act, &oact) < 0) {
100 		/* And voila -- we just tricked the kernel into giving us our
101 		 * restorer function! */
102 	} else {
103 		restore_rt = oact.sa_restorer;
104 		ret_code = 0;
105 	}
106 
107 	return ret_code;
108 }
109 
110 #endif /* __x86_64__ */
111 
112 #ifdef __sparc__
113 # if defined __arch64__ || defined __sparcv9
114 
115 /*
116  * Based on glibc/sysdeps/unix/sysv/linux/sparc/sparc64/sigaction.c
117  */
118 
119 extern char *__rt_sig_stub;
120 
__rt_sigreturn_stub(void)121 static void __attribute__((used)) __rt_sigreturn_stub(void)
122 {
123 	__asm__ ("__rt_sig_stub: mov %0, %%g1\n\t"
124 		"ta  0x6d\n\t"
125 		: /* no outputs */
126 		: "i" (__NR_rt_sigreturn));
127 }
128 
129 # else /* sparc32 */
130 
131 /*
132  * Based on glibc/sysdeps/unix/sysv/linux/sparc/sparc32/sigaction.c
133  */
134 
135 extern char *__rt_sig_stub, *__sig_stub;
136 
__rt_sigreturn_stub(void)137 static void __attribute__((used)) __rt_sigreturn_stub(void)
138 {
139 	__asm__ ("__rt_sig_stub: mov %0, %%g1\n\t"
140 		"ta  0x10\n\t"
141 		: /* no outputs */
142 		: "i" (__NR_rt_sigreturn));
143 }
144 
__sigreturn_stub(void)145 static void __attribute__((used)) __sigreturn_stub(void)
146 {
147 	__asm__ ("__sig_stub: mov %0, %%g1\n\t"
148 		"ta  0x10\n\t"
149 		: /* no outputs */
150 		: "i" (__NR_sigreturn));
151 }
152 
153 # endif
154 #endif /* __sparc__ */
155 
156 #ifdef __arc__
157 
158 #undef SA_RESTORER
159 #define SA_RESTORER     0x04000000
160 
161 /*
162  * based on uClibc/libc/sysdeps/linux/arc/sigaction.c
163  */
164 static void
restore_rt(void)165 __attribute__ ((optimize("Os"))) __attribute__((used)) restore_rt(void)
166 {
167 	__asm__ (
168 		"mov r8, %0	\n\t"
169 #ifdef __ARCHS__
170 		"trap_s	0	\n\t"
171 #else
172 		"trap0	\n\t"
173 #endif
174 		: /* no outputs */
175 		: "i" (__NR_rt_sigreturn)
176 		: "r8");
177 }
178 #endif
179 
180 #ifdef TST_TEST_H__
181 # define TST_SYSCALL tst_syscall
182 #else
183 # define TST_SYSCALL ltp_syscall
184 #endif
185 
186 /* This is a wrapper for __NR_rt_sigaction syscall.
187  * act/oact values of INVAL_SA_PTR is used to pass
188  * an invalid pointer to syscall(__NR_rt_sigaction)
189  *
190  * Based on glibc/sysdeps/unix/sysv/linux/{...}/sigaction.c
191  */
192 
ltp_rt_sigaction(int signum,const struct sigaction * act,struct sigaction * oact,size_t sigsetsize)193 static int ltp_rt_sigaction(int signum, const struct sigaction *act,
194 			struct sigaction *oact, size_t sigsetsize)
195 {
196 	int ret;
197 	struct kernel_sigaction kact, koact;
198 	struct kernel_sigaction *kact_p = NULL;
199 	struct kernel_sigaction *koact_p = NULL;
200 
201 	if (act == INVAL_SA_PTR) {
202 		kact_p = INVAL_SA_PTR;
203 	} else if (act) {
204 		kact.k_sa_handler = act->sa_handler;
205 		memcpy(&kact.sa_mask, &act->sa_mask, sizeof(sigset_t));
206 		kact.sa_flags = act->sa_flags;
207 #ifndef __mips__
208 		kact.sa_restorer = NULL;
209 #endif
210 		kact_p = &kact;
211 	}
212 
213 	if (oact == INVAL_SA_PTR)
214 		koact_p = INVAL_SA_PTR;
215 	else if (oact)
216 		koact_p = &koact;
217 
218 #ifdef __x86_64__
219 	sig_initial(signum);
220 #endif
221 
222 #if defined __x86_64__ || defined __arc__
223 	kact.sa_flags |= SA_RESTORER;
224 	kact.sa_restorer = restore_rt;
225 #endif
226 
227 #ifdef __sparc__
228 	unsigned long stub = 0;
229 # if defined __arch64__ || defined __sparcv9
230 	stub = ((unsigned long) &__rt_sig_stub) - 8;
231 # else /* sparc32 */
232 	if ((kact.sa_flags & SA_SIGINFO) != 0)
233 		stub = ((unsigned long) &__rt_sig_stub) - 8;
234 	else
235 		stub = ((unsigned long) &__sig_stub) - 8;
236 # endif
237 #endif
238 
239 
240 #ifdef __sparc__
241 	ret = TST_SYSCALL(__NR_rt_sigaction, signum,
242 			kact_p, koact_p,
243 			stub, sigsetsize);
244 #else
245 	ret = TST_SYSCALL(__NR_rt_sigaction, signum,
246 			kact_p, koact_p,
247 			sigsetsize);
248 #endif
249 
250 	if (ret >= 0) {
251 		if (oact && (oact != INVAL_SA_PTR)) {
252 			oact->sa_handler = koact.k_sa_handler;
253 			memcpy(&oact->sa_mask, &koact.sa_mask,
254 				sizeof(sigset_t));
255 			oact->sa_flags = koact.sa_flags;
256 #ifdef HAVE_SA_RESTORER
257 			oact->sa_restorer = koact.sa_restorer;
258 #endif
259 		}
260 	}
261 
262 	return ret;
263 }
264 
265 #endif /* LTP_RT_SIGACTION_H */
266