diff options
Diffstat (limited to 'arch/m68k/kernel')
-rw-r--r-- | arch/m68k/kernel/signal.c | 173 |
1 files changed, 61 insertions, 112 deletions
diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 16ea319d1ed5..d5f4a82515f3 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c | |||
@@ -286,36 +286,10 @@ out: | |||
286 | return err; | 286 | return err; |
287 | } | 287 | } |
288 | 288 | ||
289 | static inline int | 289 | static int mangle_kernel_stack(struct pt_regs *regs, int formatvec, |
290 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp, | 290 | void __user *fp) |
291 | int *pd0) | ||
292 | { | 291 | { |
293 | int fsize, formatvec; | 292 | int fsize = frame_extra_sizes[formatvec >> 12]; |
294 | struct sigcontext context; | ||
295 | int err; | ||
296 | |||
297 | /* Always make any pending restarted system calls return -EINTR */ | ||
298 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
299 | |||
300 | /* get previous context */ | ||
301 | if (copy_from_user(&context, usc, sizeof(context))) | ||
302 | goto badframe; | ||
303 | |||
304 | /* restore passed registers */ | ||
305 | regs->d1 = context.sc_d1; | ||
306 | regs->a0 = context.sc_a0; | ||
307 | regs->a1 = context.sc_a1; | ||
308 | regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); | ||
309 | regs->pc = context.sc_pc; | ||
310 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
311 | wrusp(context.sc_usp); | ||
312 | formatvec = context.sc_formatvec; | ||
313 | regs->format = formatvec >> 12; | ||
314 | regs->vector = formatvec & 0xfff; | ||
315 | |||
316 | err = restore_fpu_state(&context); | ||
317 | |||
318 | fsize = frame_extra_sizes[regs->format]; | ||
319 | if (fsize < 0) { | 293 | if (fsize < 0) { |
320 | /* | 294 | /* |
321 | * user process trying to return with weird frame format | 295 | * user process trying to return with weird frame format |
@@ -323,16 +297,22 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u | |||
323 | #ifdef DEBUG | 297 | #ifdef DEBUG |
324 | printk("user process returning with weird frame format\n"); | 298 | printk("user process returning with weird frame format\n"); |
325 | #endif | 299 | #endif |
326 | goto badframe; | 300 | return 1; |
327 | } | 301 | } |
302 | if (!fsize) { | ||
303 | regs->format = formatvec >> 12; | ||
304 | regs->vector = formatvec & 0xfff; | ||
305 | } else { | ||
306 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
307 | unsigned long buf[fsize / 2]; /* yes, twice as much */ | ||
328 | 308 | ||
329 | /* OK. Make room on the supervisor stack for the extra junk, | 309 | /* that'll make sure that expansion won't crap over data */ |
330 | * if necessary. | 310 | if (copy_from_user(buf + fsize / 4, fp, fsize)) |
331 | */ | 311 | return 1; |
332 | 312 | ||
333 | if (fsize) { | 313 | /* point of no return */ |
334 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | 314 | regs->format = formatvec >> 12; |
335 | regs->d0 = context.sc_d0; | 315 | regs->vector = formatvec & 0xfff; |
336 | #define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) | 316 | #define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) |
337 | __asm__ __volatile__ | 317 | __asm__ __volatile__ |
338 | (" movel %0,%/a0\n\t" | 318 | (" movel %0,%/a0\n\t" |
@@ -344,30 +324,50 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __u | |||
344 | " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ | 324 | " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ |
345 | " lsrl #2,%1\n\t" | 325 | " lsrl #2,%1\n\t" |
346 | " subql #1,%1\n\t" | 326 | " subql #1,%1\n\t" |
347 | "2: movesl %4@+,%2\n\t" | 327 | /* copy to the gap we'd made */ |
348 | "3: movel %2,%/a0@+\n\t" | 328 | "2: movel %4@+,%/a0@+\n\t" |
349 | " dbra %1,2b\n\t" | 329 | " dbra %1,2b\n\t" |
350 | " bral ret_from_signal\n" | 330 | " bral ret_from_signal\n" |
351 | "4:\n" | ||
352 | ".section __ex_table,\"a\"\n" | ||
353 | " .align 4\n" | ||
354 | " .long 2b,4b\n" | ||
355 | " .long 3b,4b\n" | ||
356 | ".previous" | ||
357 | : /* no outputs, it doesn't ever return */ | 331 | : /* no outputs, it doesn't ever return */ |
358 | : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), | 332 | : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), |
359 | "n" (frame_offset), "a" (fp) | 333 | "n" (frame_offset), "a" (buf + fsize/4) |
360 | : "a0"); | 334 | : "a0"); |
361 | #undef frame_offset | 335 | #undef frame_offset |
362 | /* | ||
363 | * If we ever get here an exception occurred while | ||
364 | * building the above stack-frame. | ||
365 | */ | ||
366 | goto badframe; | ||
367 | } | 336 | } |
337 | return 0; | ||
338 | } | ||
368 | 339 | ||
369 | *pd0 = context.sc_d0; | 340 | static inline int |
370 | return err; | 341 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp) |
342 | { | ||
343 | int formatvec; | ||
344 | struct sigcontext context; | ||
345 | int err; | ||
346 | |||
347 | /* Always make any pending restarted system calls return -EINTR */ | ||
348 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
349 | |||
350 | /* get previous context */ | ||
351 | if (copy_from_user(&context, usc, sizeof(context))) | ||
352 | goto badframe; | ||
353 | |||
354 | /* restore passed registers */ | ||
355 | regs->d0 = context.sc_d0; | ||
356 | regs->d1 = context.sc_d1; | ||
357 | regs->a0 = context.sc_a0; | ||
358 | regs->a1 = context.sc_a1; | ||
359 | regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); | ||
360 | regs->pc = context.sc_pc; | ||
361 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
362 | wrusp(context.sc_usp); | ||
363 | formatvec = context.sc_formatvec; | ||
364 | |||
365 | err = restore_fpu_state(&context); | ||
366 | |||
367 | if (err || mangle_kernel_stack(regs, formatvec, fp)) | ||
368 | goto badframe; | ||
369 | |||
370 | return 0; | ||
371 | 371 | ||
372 | badframe: | 372 | badframe: |
373 | return 1; | 373 | return 1; |
@@ -375,9 +375,9 @@ badframe: | |||
375 | 375 | ||
376 | static inline int | 376 | static inline int |
377 | rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, | 377 | rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, |
378 | struct ucontext __user *uc, int *pd0) | 378 | struct ucontext __user *uc) |
379 | { | 379 | { |
380 | int fsize, temp; | 380 | int temp; |
381 | greg_t __user *gregs = uc->uc_mcontext.gregs; | 381 | greg_t __user *gregs = uc->uc_mcontext.gregs; |
382 | unsigned long usp; | 382 | unsigned long usp; |
383 | int err; | 383 | int err; |
@@ -411,65 +411,16 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, | |||
411 | regs->sr = (regs->sr & 0xff00) | (temp & 0xff); | 411 | regs->sr = (regs->sr & 0xff00) | (temp & 0xff); |
412 | regs->orig_d0 = -1; /* disable syscall checks */ | 412 | regs->orig_d0 = -1; /* disable syscall checks */ |
413 | err |= __get_user(temp, &uc->uc_formatvec); | 413 | err |= __get_user(temp, &uc->uc_formatvec); |
414 | regs->format = temp >> 12; | ||
415 | regs->vector = temp & 0xfff; | ||
416 | 414 | ||
417 | err |= rt_restore_fpu_state(uc); | 415 | err |= rt_restore_fpu_state(uc); |
418 | 416 | ||
419 | if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) | 417 | if (err || do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) |
420 | goto badframe; | 418 | goto badframe; |
421 | 419 | ||
422 | fsize = frame_extra_sizes[regs->format]; | 420 | if (mangle_kernel_stack(regs, temp, &uc->uc_extra)) |
423 | if (fsize < 0) { | ||
424 | /* | ||
425 | * user process trying to return with weird frame format | ||
426 | */ | ||
427 | #ifdef DEBUG | ||
428 | printk("user process returning with weird frame format\n"); | ||
429 | #endif | ||
430 | goto badframe; | 421 | goto badframe; |
431 | } | ||
432 | 422 | ||
433 | /* OK. Make room on the supervisor stack for the extra junk, | 423 | return 0; |
434 | * if necessary. | ||
435 | */ | ||
436 | |||
437 | if (fsize) { | ||
438 | #define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) | ||
439 | __asm__ __volatile__ | ||
440 | (" movel %0,%/a0\n\t" | ||
441 | " subl %1,%/a0\n\t" /* make room on stack */ | ||
442 | " movel %/a0,%/sp\n\t" /* set stack pointer */ | ||
443 | /* move switch_stack and pt_regs */ | ||
444 | "1: movel %0@+,%/a0@+\n\t" | ||
445 | " dbra %2,1b\n\t" | ||
446 | " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ | ||
447 | " lsrl #2,%1\n\t" | ||
448 | " subql #1,%1\n\t" | ||
449 | "2: movesl %4@+,%2\n\t" | ||
450 | "3: movel %2,%/a0@+\n\t" | ||
451 | " dbra %1,2b\n\t" | ||
452 | " bral ret_from_signal\n" | ||
453 | "4:\n" | ||
454 | ".section __ex_table,\"a\"\n" | ||
455 | " .align 4\n" | ||
456 | " .long 2b,4b\n" | ||
457 | " .long 3b,4b\n" | ||
458 | ".previous" | ||
459 | : /* no outputs, it doesn't ever return */ | ||
460 | : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), | ||
461 | "n" (frame_offset), "a" (&uc->uc_extra) | ||
462 | : "a0"); | ||
463 | #undef frame_offset | ||
464 | /* | ||
465 | * If we ever get here an exception occurred while | ||
466 | * building the above stack-frame. | ||
467 | */ | ||
468 | goto badframe; | ||
469 | } | ||
470 | |||
471 | *pd0 = regs->d0; | ||
472 | return err; | ||
473 | 424 | ||
474 | badframe: | 425 | badframe: |
475 | return 1; | 426 | return 1; |
@@ -482,7 +433,6 @@ asmlinkage int do_sigreturn(unsigned long __unused) | |||
482 | unsigned long usp = rdusp(); | 433 | unsigned long usp = rdusp(); |
483 | struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); | 434 | struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); |
484 | sigset_t set; | 435 | sigset_t set; |
485 | int d0; | ||
486 | 436 | ||
487 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 437 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
488 | goto badframe; | 438 | goto badframe; |
@@ -496,9 +446,9 @@ asmlinkage int do_sigreturn(unsigned long __unused) | |||
496 | current->blocked = set; | 446 | current->blocked = set; |
497 | recalc_sigpending(); | 447 | recalc_sigpending(); |
498 | 448 | ||
499 | if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) | 449 | if (restore_sigcontext(regs, &frame->sc, frame + 1)) |
500 | goto badframe; | 450 | goto badframe; |
501 | return d0; | 451 | return regs->d0; |
502 | 452 | ||
503 | badframe: | 453 | badframe: |
504 | force_sig(SIGSEGV, current); | 454 | force_sig(SIGSEGV, current); |
@@ -512,7 +462,6 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused) | |||
512 | unsigned long usp = rdusp(); | 462 | unsigned long usp = rdusp(); |
513 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); | 463 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); |
514 | sigset_t set; | 464 | sigset_t set; |
515 | int d0; | ||
516 | 465 | ||
517 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 466 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
518 | goto badframe; | 467 | goto badframe; |
@@ -523,9 +472,9 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused) | |||
523 | current->blocked = set; | 472 | current->blocked = set; |
524 | recalc_sigpending(); | 473 | recalc_sigpending(); |
525 | 474 | ||
526 | if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) | 475 | if (rt_restore_ucontext(regs, sw, &frame->uc)) |
527 | goto badframe; | 476 | goto badframe; |
528 | return d0; | 477 | return regs->d0; |
529 | 478 | ||
530 | badframe: | 479 | badframe: |
531 | force_sig(SIGSEGV, current); | 480 | force_sig(SIGSEGV, current); |