diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
-rw-r--r-- | arch/mips/kernel/signal.c | 272 |
1 files changed, 205 insertions, 67 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index b9d358e05214..adbfb95e42d0 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -34,18 +34,192 @@ | |||
34 | 34 | ||
35 | #include "signal-common.h" | 35 | #include "signal-common.h" |
36 | 36 | ||
37 | #define DEBUG_SIG 0 | 37 | /* |
38 | * Horribly complicated - with the bloody RM9000 workarounds enabled | ||
39 | * the signal trampolines is moving to the end of the structure so we can | ||
40 | * increase the alignment without breaking software compatibility. | ||
41 | */ | ||
42 | #if ICACHE_REFILLS_WORKAROUND_WAR == 0 | ||
43 | |||
44 | struct sigframe { | ||
45 | u32 sf_ass[4]; /* argument save space for o32 */ | ||
46 | u32 sf_code[2]; /* signal trampoline */ | ||
47 | struct sigcontext sf_sc; | ||
48 | sigset_t sf_mask; | ||
49 | }; | ||
50 | |||
51 | struct rt_sigframe { | ||
52 | u32 rs_ass[4]; /* argument save space for o32 */ | ||
53 | u32 rs_code[2]; /* signal trampoline */ | ||
54 | struct siginfo rs_info; | ||
55 | struct ucontext rs_uc; | ||
56 | }; | ||
38 | 57 | ||
39 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 58 | #else |
59 | |||
60 | struct sigframe { | ||
61 | u32 sf_ass[4]; /* argument save space for o32 */ | ||
62 | u32 sf_pad[2]; | ||
63 | struct sigcontext sf_sc; /* hw context */ | ||
64 | sigset_t sf_mask; | ||
65 | u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
66 | }; | ||
67 | |||
68 | struct rt_sigframe { | ||
69 | u32 rs_ass[4]; /* argument save space for o32 */ | ||
70 | u32 rs_pad[2]; | ||
71 | struct siginfo rs_info; | ||
72 | struct ucontext rs_uc; | ||
73 | u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
74 | }; | ||
75 | |||
76 | #endif | ||
77 | |||
78 | /* | ||
79 | * Helper routines | ||
80 | */ | ||
81 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | ||
82 | { | ||
83 | int err = 0; | ||
84 | int i; | ||
85 | |||
86 | err |= __put_user(regs->cp0_epc, &sc->sc_pc); | ||
87 | |||
88 | err |= __put_user(0, &sc->sc_regs[0]); | ||
89 | for (i = 1; i < 32; i++) | ||
90 | err |= __put_user(regs->regs[i], &sc->sc_regs[i]); | ||
91 | |||
92 | err |= __put_user(regs->hi, &sc->sc_mdhi); | ||
93 | err |= __put_user(regs->lo, &sc->sc_mdlo); | ||
94 | if (cpu_has_dsp) { | ||
95 | err |= __put_user(mfhi1(), &sc->sc_hi1); | ||
96 | err |= __put_user(mflo1(), &sc->sc_lo1); | ||
97 | err |= __put_user(mfhi2(), &sc->sc_hi2); | ||
98 | err |= __put_user(mflo2(), &sc->sc_lo2); | ||
99 | err |= __put_user(mfhi3(), &sc->sc_hi3); | ||
100 | err |= __put_user(mflo3(), &sc->sc_lo3); | ||
101 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | ||
102 | } | ||
103 | |||
104 | err |= __put_user(!!used_math(), &sc->sc_used_math); | ||
105 | |||
106 | if (used_math()) { | ||
107 | /* | ||
108 | * Save FPU state to signal context. Signal handler | ||
109 | * will "inherit" current FPU state. | ||
110 | */ | ||
111 | preempt_disable(); | ||
112 | |||
113 | if (!is_fpu_owner()) { | ||
114 | own_fpu(); | ||
115 | restore_fp(current); | ||
116 | } | ||
117 | err |= save_fp_context(sc); | ||
118 | |||
119 | preempt_enable(); | ||
120 | } | ||
121 | return err; | ||
122 | } | ||
123 | |||
124 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | ||
125 | { | ||
126 | unsigned int used_math; | ||
127 | unsigned long treg; | ||
128 | int err = 0; | ||
129 | int i; | ||
130 | |||
131 | /* Always make any pending restarted system calls return -EINTR */ | ||
132 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
133 | |||
134 | err |= __get_user(regs->cp0_epc, &sc->sc_pc); | ||
135 | err |= __get_user(regs->hi, &sc->sc_mdhi); | ||
136 | err |= __get_user(regs->lo, &sc->sc_mdlo); | ||
137 | if (cpu_has_dsp) { | ||
138 | err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); | ||
139 | err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); | ||
140 | err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); | ||
141 | err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); | ||
142 | err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); | ||
143 | err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); | ||
144 | err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); | ||
145 | } | ||
146 | |||
147 | for (i = 1; i < 32; i++) | ||
148 | err |= __get_user(regs->regs[i], &sc->sc_regs[i]); | ||
149 | |||
150 | err |= __get_user(used_math, &sc->sc_used_math); | ||
151 | conditional_used_math(used_math); | ||
152 | |||
153 | preempt_disable(); | ||
154 | |||
155 | if (used_math()) { | ||
156 | /* restore fpu context if we have used it before */ | ||
157 | own_fpu(); | ||
158 | err |= restore_fp_context(sc); | ||
159 | } else { | ||
160 | /* signal handler may have used FPU. Give it up. */ | ||
161 | lose_fpu(); | ||
162 | } | ||
163 | |||
164 | preempt_enable(); | ||
165 | |||
166 | return err; | ||
167 | } | ||
168 | |||
169 | void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | ||
170 | size_t frame_size) | ||
171 | { | ||
172 | unsigned long sp; | ||
173 | |||
174 | /* Default to using normal stack */ | ||
175 | sp = regs->regs[29]; | ||
176 | |||
177 | /* | ||
178 | * FPU emulator may have it's own trampoline active just | ||
179 | * above the user stack, 16-bytes before the next lowest | ||
180 | * 16 byte boundary. Try to avoid trashing it. | ||
181 | */ | ||
182 | sp -= 32; | ||
183 | |||
184 | /* This is the X/Open sanctioned signal stack switching. */ | ||
185 | if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0)) | ||
186 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
187 | |||
188 | return (void __user *)((sp - frame_size) & (ICACHE_REFILLS_WORKAROUND_WAR ? ~(cpu_icache_line_size()-1) : ALMASK)); | ||
189 | } | ||
190 | |||
191 | int install_sigtramp(unsigned int __user *tramp, unsigned int syscall) | ||
192 | { | ||
193 | int err; | ||
194 | |||
195 | /* | ||
196 | * Set up the return code ... | ||
197 | * | ||
198 | * li v0, __NR__foo_sigreturn | ||
199 | * syscall | ||
200 | */ | ||
201 | |||
202 | err = __put_user(0x24020000 + syscall, tramp + 0); | ||
203 | err |= __put_user(0x0000000c , tramp + 1); | ||
204 | if (ICACHE_REFILLS_WORKAROUND_WAR) { | ||
205 | err |= __put_user(0, tramp + 2); | ||
206 | err |= __put_user(0, tramp + 3); | ||
207 | err |= __put_user(0, tramp + 4); | ||
208 | err |= __put_user(0, tramp + 5); | ||
209 | err |= __put_user(0, tramp + 6); | ||
210 | err |= __put_user(0, tramp + 7); | ||
211 | } | ||
212 | flush_cache_sigtramp((unsigned long) tramp); | ||
213 | |||
214 | return err; | ||
215 | } | ||
40 | 216 | ||
41 | /* | 217 | /* |
42 | * Atomically swap in the new signal mask, and wait for a signal. | 218 | * Atomically swap in the new signal mask, and wait for a signal. |
43 | */ | 219 | */ |
44 | 220 | ||
45 | #ifdef CONFIG_TRAD_SIGNALS | 221 | #ifdef CONFIG_TRAD_SIGNALS |
46 | save_static_function(sys_sigsuspend); | 222 | asmlinkage int sys_sigsuspend(nabi_no_regargs struct pt_regs regs) |
47 | __attribute_used__ noinline static int | ||
48 | _sys_sigsuspend(nabi_no_regargs struct pt_regs regs) | ||
49 | { | 223 | { |
50 | sigset_t newset; | 224 | sigset_t newset; |
51 | sigset_t __user *uset; | 225 | sigset_t __user *uset; |
@@ -68,9 +242,7 @@ _sys_sigsuspend(nabi_no_regargs struct pt_regs regs) | |||
68 | } | 242 | } |
69 | #endif | 243 | #endif |
70 | 244 | ||
71 | save_static_function(sys_rt_sigsuspend); | 245 | asmlinkage int sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) |
72 | __attribute_used__ noinline static int | ||
73 | _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) | ||
74 | { | 246 | { |
75 | sigset_t newset; | 247 | sigset_t newset; |
76 | sigset_t __user *unewset; | 248 | sigset_t __user *unewset; |
@@ -89,7 +261,7 @@ _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) | |||
89 | spin_lock_irq(¤t->sighand->siglock); | 261 | spin_lock_irq(¤t->sighand->siglock); |
90 | current->saved_sigmask = current->blocked; | 262 | current->saved_sigmask = current->blocked; |
91 | current->blocked = newset; | 263 | current->blocked = newset; |
92 | recalc_sigpending(); | 264 | recalc_sigpending(); |
93 | spin_unlock_irq(¤t->sighand->siglock); | 265 | spin_unlock_irq(¤t->sighand->siglock); |
94 | 266 | ||
95 | current->state = TASK_INTERRUPTIBLE; | 267 | current->state = TASK_INTERRUPTIBLE; |
@@ -124,7 +296,7 @@ asmlinkage int sys_sigaction(int sig, const struct sigaction __user *act, | |||
124 | 296 | ||
125 | if (!ret && oact) { | 297 | if (!ret && oact) { |
126 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) | 298 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) |
127 | return -EFAULT; | 299 | return -EFAULT; |
128 | err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); | 300 | err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); |
129 | err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); | 301 | err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); |
130 | err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); | 302 | err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); |
@@ -148,45 +320,8 @@ asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs) | |||
148 | return do_sigaltstack(uss, uoss, usp); | 320 | return do_sigaltstack(uss, uoss, usp); |
149 | } | 321 | } |
150 | 322 | ||
151 | /* | ||
152 | * Horribly complicated - with the bloody RM9000 workarounds enabled | ||
153 | * the signal trampolines is moving to the end of the structure so we can | ||
154 | * increase the alignment without breaking software compatibility. | ||
155 | */ | ||
156 | #ifdef CONFIG_TRAD_SIGNALS | ||
157 | struct sigframe { | ||
158 | u32 sf_ass[4]; /* argument save space for o32 */ | ||
159 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
160 | u32 sf_pad[2]; | ||
161 | #else | ||
162 | u32 sf_code[2]; /* signal trampoline */ | ||
163 | #endif | ||
164 | struct sigcontext sf_sc; | ||
165 | sigset_t sf_mask; | ||
166 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
167 | u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
168 | #endif | ||
169 | }; | ||
170 | #endif | ||
171 | |||
172 | struct rt_sigframe { | ||
173 | u32 rs_ass[4]; /* argument save space for o32 */ | ||
174 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
175 | u32 rs_pad[2]; | ||
176 | #else | ||
177 | u32 rs_code[2]; /* signal trampoline */ | ||
178 | #endif | ||
179 | struct siginfo rs_info; | ||
180 | struct ucontext rs_uc; | ||
181 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
182 | u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
183 | #endif | ||
184 | }; | ||
185 | |||
186 | #ifdef CONFIG_TRAD_SIGNALS | 323 | #ifdef CONFIG_TRAD_SIGNALS |
187 | save_static_function(sys_sigreturn); | 324 | asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) |
188 | __attribute_used__ noinline static void | ||
189 | _sys_sigreturn(nabi_no_regargs struct pt_regs regs) | ||
190 | { | 325 | { |
191 | struct sigframe __user *frame; | 326 | struct sigframe __user *frame; |
192 | sigset_t blocked; | 327 | sigset_t blocked; |
@@ -221,9 +356,7 @@ badframe: | |||
221 | } | 356 | } |
222 | #endif /* CONFIG_TRAD_SIGNALS */ | 357 | #endif /* CONFIG_TRAD_SIGNALS */ |
223 | 358 | ||
224 | save_static_function(sys_rt_sigreturn); | 359 | asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) |
225 | __attribute_used__ noinline static void | ||
226 | _sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | ||
227 | { | 360 | { |
228 | struct rt_sigframe __user *frame; | 361 | struct rt_sigframe __user *frame; |
229 | sigset_t set; | 362 | sigset_t set; |
@@ -265,7 +398,7 @@ badframe: | |||
265 | } | 398 | } |
266 | 399 | ||
267 | #ifdef CONFIG_TRAD_SIGNALS | 400 | #ifdef CONFIG_TRAD_SIGNALS |
268 | int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | 401 | static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, |
269 | int signr, sigset_t *set) | 402 | int signr, sigset_t *set) |
270 | { | 403 | { |
271 | struct sigframe __user *frame; | 404 | struct sigframe __user *frame; |
@@ -275,7 +408,7 @@ int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
275 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 408 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
276 | goto give_sigsegv; | 409 | goto give_sigsegv; |
277 | 410 | ||
278 | install_sigtramp(frame->sf_code, __NR_sigreturn); | 411 | err |= install_sigtramp(frame->sf_code, __NR_sigreturn); |
279 | 412 | ||
280 | err |= setup_sigcontext(regs, &frame->sf_sc); | 413 | err |= setup_sigcontext(regs, &frame->sf_sc); |
281 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); | 414 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); |
@@ -299,12 +432,10 @@ int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
299 | regs->regs[31] = (unsigned long) frame->sf_code; | 432 | regs->regs[31] = (unsigned long) frame->sf_code; |
300 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; | 433 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; |
301 | 434 | ||
302 | #if DEBUG_SIG | 435 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", |
303 | printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", | ||
304 | current->comm, current->pid, | 436 | current->comm, current->pid, |
305 | frame, regs->cp0_epc, frame->regs[31]); | 437 | frame, regs->cp0_epc, regs->regs[31]); |
306 | #endif | 438 | return 0; |
307 | return 0; | ||
308 | 439 | ||
309 | give_sigsegv: | 440 | give_sigsegv: |
310 | force_sigsegv(signr, current); | 441 | force_sigsegv(signr, current); |
@@ -312,7 +443,7 @@ give_sigsegv: | |||
312 | } | 443 | } |
313 | #endif | 444 | #endif |
314 | 445 | ||
315 | int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | 446 | static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, |
316 | int signr, sigset_t *set, siginfo_t *info) | 447 | int signr, sigset_t *set, siginfo_t *info) |
317 | { | 448 | { |
318 | struct rt_sigframe __user *frame; | 449 | struct rt_sigframe __user *frame; |
@@ -322,7 +453,7 @@ int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
322 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 453 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
323 | goto give_sigsegv; | 454 | goto give_sigsegv; |
324 | 455 | ||
325 | install_sigtramp(frame->rs_code, __NR_rt_sigreturn); | 456 | err |= install_sigtramp(frame->rs_code, __NR_rt_sigreturn); |
326 | 457 | ||
327 | /* Create siginfo. */ | 458 | /* Create siginfo. */ |
328 | err |= copy_siginfo_to_user(&frame->rs_info, info); | 459 | err |= copy_siginfo_to_user(&frame->rs_info, info); |
@@ -359,11 +490,10 @@ int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
359 | regs->regs[31] = (unsigned long) frame->rs_code; | 490 | regs->regs[31] = (unsigned long) frame->rs_code; |
360 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; | 491 | regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; |
361 | 492 | ||
362 | #if DEBUG_SIG | 493 | DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n", |
363 | printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n", | ||
364 | current->comm, current->pid, | 494 | current->comm, current->pid, |
365 | frame, regs->cp0_epc, regs->regs[31]); | 495 | frame, regs->cp0_epc, regs->regs[31]); |
366 | #endif | 496 | |
367 | return 0; | 497 | return 0; |
368 | 498 | ||
369 | give_sigsegv: | 499 | give_sigsegv: |
@@ -371,7 +501,15 @@ give_sigsegv: | |||
371 | return -EFAULT; | 501 | return -EFAULT; |
372 | } | 502 | } |
373 | 503 | ||
374 | static inline int handle_signal(unsigned long sig, siginfo_t *info, | 504 | struct mips_abi mips_abi = { |
505 | #ifdef CONFIG_TRAD_SIGNALS | ||
506 | .setup_frame = setup_frame, | ||
507 | #endif | ||
508 | .setup_rt_frame = setup_rt_frame, | ||
509 | .restart = __NR_restart_syscall | ||
510 | }; | ||
511 | |||
512 | static int handle_signal(unsigned long sig, siginfo_t *info, | ||
375 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) | 513 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) |
376 | { | 514 | { |
377 | int ret; | 515 | int ret; |
@@ -409,7 +547,7 @@ static inline int handle_signal(unsigned long sig, siginfo_t *info, | |||
409 | return ret; | 547 | return ret; |
410 | } | 548 | } |
411 | 549 | ||
412 | void do_signal(struct pt_regs *regs) | 550 | static void do_signal(struct pt_regs *regs) |
413 | { | 551 | { |
414 | struct k_sigaction ka; | 552 | struct k_sigaction ka; |
415 | sigset_t *oldset; | 553 | sigset_t *oldset; |
@@ -459,7 +597,7 @@ void do_signal(struct pt_regs *regs) | |||
459 | regs->cp0_epc -= 8; | 597 | regs->cp0_epc -= 8; |
460 | } | 598 | } |
461 | if (regs->regs[2] == ERESTART_RESTARTBLOCK) { | 599 | if (regs->regs[2] == ERESTART_RESTARTBLOCK) { |
462 | regs->regs[2] = __NR_restart_syscall; | 600 | regs->regs[2] = current->thread.abi->restart; |
463 | regs->regs[7] = regs->regs[26]; | 601 | regs->regs[7] = regs->regs[26]; |
464 | regs->cp0_epc -= 4; | 602 | regs->cp0_epc -= 4; |
465 | } | 603 | } |
@@ -485,5 +623,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |||
485 | { | 623 | { |
486 | /* deal with pending signal delivery */ | 624 | /* deal with pending signal delivery */ |
487 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) | 625 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) |
488 | current->thread.abi->do_signal(regs); | 626 | do_signal(regs); |
489 | } | 627 | } |