diff options
Diffstat (limited to 'arch/mips/kernel/ptrace.c')
-rw-r--r-- | arch/mips/kernel/ptrace.c | 147 |
1 files changed, 122 insertions, 25 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index a3f38e6b7ea1..c3d2d2c05fdb 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -439,63 +439,160 @@ static int gpr64_set(struct task_struct *target, | |||
439 | 439 | ||
440 | #endif /* CONFIG_64BIT */ | 440 | #endif /* CONFIG_64BIT */ |
441 | 441 | ||
442 | /* | ||
443 | * Copy the floating-point context to the supplied NT_PRFPREG buffer, | ||
444 | * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots | ||
445 | * correspond 1:1 to buffer slots. Only general registers are copied. | ||
446 | */ | ||
447 | static int fpr_get_fpa(struct task_struct *target, | ||
448 | unsigned int *pos, unsigned int *count, | ||
449 | void **kbuf, void __user **ubuf) | ||
450 | { | ||
451 | return user_regset_copyout(pos, count, kbuf, ubuf, | ||
452 | &target->thread.fpu, | ||
453 | 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * Copy the floating-point context to the supplied NT_PRFPREG buffer, | ||
458 | * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's | ||
459 | * general register slots are copied to buffer slots. Only general | ||
460 | * registers are copied. | ||
461 | */ | ||
462 | static int fpr_get_msa(struct task_struct *target, | ||
463 | unsigned int *pos, unsigned int *count, | ||
464 | void **kbuf, void __user **ubuf) | ||
465 | { | ||
466 | unsigned int i; | ||
467 | u64 fpr_val; | ||
468 | int err; | ||
469 | |||
470 | BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); | ||
471 | for (i = 0; i < NUM_FPU_REGS; i++) { | ||
472 | fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); | ||
473 | err = user_regset_copyout(pos, count, kbuf, ubuf, | ||
474 | &fpr_val, i * sizeof(elf_fpreg_t), | ||
475 | (i + 1) * sizeof(elf_fpreg_t)); | ||
476 | if (err) | ||
477 | return err; | ||
478 | } | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * Copy the floating-point context to the supplied NT_PRFPREG buffer. | ||
485 | * Choose the appropriate helper for general registers, and then copy | ||
486 | * the FCSR register separately. | ||
487 | */ | ||
442 | static int fpr_get(struct task_struct *target, | 488 | static int fpr_get(struct task_struct *target, |
443 | const struct user_regset *regset, | 489 | const struct user_regset *regset, |
444 | unsigned int pos, unsigned int count, | 490 | unsigned int pos, unsigned int count, |
445 | void *kbuf, void __user *ubuf) | 491 | void *kbuf, void __user *ubuf) |
446 | { | 492 | { |
447 | unsigned i; | 493 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
448 | int err; | 494 | int err; |
449 | u64 fpr_val; | ||
450 | 495 | ||
451 | /* XXX fcr31 */ | 496 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) |
497 | err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf); | ||
498 | else | ||
499 | err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf); | ||
500 | if (err) | ||
501 | return err; | ||
452 | 502 | ||
453 | if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) | 503 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
454 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 504 | &target->thread.fpu.fcr31, |
455 | &target->thread.fpu, | 505 | fcr31_pos, fcr31_pos + sizeof(u32)); |
456 | 0, sizeof(elf_fpregset_t)); | ||
457 | 506 | ||
458 | for (i = 0; i < NUM_FPU_REGS; i++) { | 507 | return err; |
459 | fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); | 508 | } |
460 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 509 | |
461 | &fpr_val, i * sizeof(elf_fpreg_t), | 510 | /* |
462 | (i + 1) * sizeof(elf_fpreg_t)); | 511 | * Copy the supplied NT_PRFPREG buffer to the floating-point context, |
512 | * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP | ||
513 | * context's general register slots. Only general registers are copied. | ||
514 | */ | ||
515 | static int fpr_set_fpa(struct task_struct *target, | ||
516 | unsigned int *pos, unsigned int *count, | ||
517 | const void **kbuf, const void __user **ubuf) | ||
518 | { | ||
519 | return user_regset_copyin(pos, count, kbuf, ubuf, | ||
520 | &target->thread.fpu, | ||
521 | 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * Copy the supplied NT_PRFPREG buffer to the floating-point context, | ||
526 | * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64 | ||
527 | * bits only of FP context's general register slots. Only general | ||
528 | * registers are copied. | ||
529 | */ | ||
530 | static int fpr_set_msa(struct task_struct *target, | ||
531 | unsigned int *pos, unsigned int *count, | ||
532 | const void **kbuf, const void __user **ubuf) | ||
533 | { | ||
534 | unsigned int i; | ||
535 | u64 fpr_val; | ||
536 | int err; | ||
537 | |||
538 | BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); | ||
539 | for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) { | ||
540 | err = user_regset_copyin(pos, count, kbuf, ubuf, | ||
541 | &fpr_val, i * sizeof(elf_fpreg_t), | ||
542 | (i + 1) * sizeof(elf_fpreg_t)); | ||
463 | if (err) | 543 | if (err) |
464 | return err; | 544 | return err; |
545 | set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); | ||
465 | } | 546 | } |
466 | 547 | ||
467 | return 0; | 548 | return 0; |
468 | } | 549 | } |
469 | 550 | ||
551 | /* | ||
552 | * Copy the supplied NT_PRFPREG buffer to the floating-point context. | ||
553 | * Choose the appropriate helper for general registers, and then copy | ||
554 | * the FCSR register separately. | ||
555 | * | ||
556 | * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', | ||
557 | * which is supposed to have been guaranteed by the kernel before | ||
558 | * calling us, e.g. in `ptrace_regset'. We enforce that requirement, | ||
559 | * so that we can safely avoid preinitializing temporaries for | ||
560 | * partial register writes. | ||
561 | */ | ||
470 | static int fpr_set(struct task_struct *target, | 562 | static int fpr_set(struct task_struct *target, |
471 | const struct user_regset *regset, | 563 | const struct user_regset *regset, |
472 | unsigned int pos, unsigned int count, | 564 | unsigned int pos, unsigned int count, |
473 | const void *kbuf, const void __user *ubuf) | 565 | const void *kbuf, const void __user *ubuf) |
474 | { | 566 | { |
475 | unsigned i; | 567 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
568 | u32 fcr31; | ||
476 | int err; | 569 | int err; |
477 | u64 fpr_val; | ||
478 | 570 | ||
479 | /* XXX fcr31 */ | 571 | BUG_ON(count % sizeof(elf_fpreg_t)); |
572 | |||
573 | if (pos + count > sizeof(elf_fpregset_t)) | ||
574 | return -EIO; | ||
480 | 575 | ||
481 | init_fp_ctx(target); | 576 | init_fp_ctx(target); |
482 | 577 | ||
483 | if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) | 578 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) |
484 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 579 | err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf); |
485 | &target->thread.fpu, | 580 | else |
486 | 0, sizeof(elf_fpregset_t)); | 581 | err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf); |
582 | if (err) | ||
583 | return err; | ||
487 | 584 | ||
488 | BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); | 585 | if (count > 0) { |
489 | for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) { | ||
490 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | 586 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
491 | &fpr_val, i * sizeof(elf_fpreg_t), | 587 | &fcr31, |
492 | (i + 1) * sizeof(elf_fpreg_t)); | 588 | fcr31_pos, fcr31_pos + sizeof(u32)); |
493 | if (err) | 589 | if (err) |
494 | return err; | 590 | return err; |
495 | set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); | 591 | |
592 | ptrace_setfcr31(target, fcr31); | ||
496 | } | 593 | } |
497 | 594 | ||
498 | return 0; | 595 | return err; |
499 | } | 596 | } |
500 | 597 | ||
501 | enum mips_regset { | 598 | enum mips_regset { |