diff options
| author | Tony Luck <tony.luck@intel.com> | 2008-04-17 13:12:44 -0400 |
|---|---|---|
| committer | Tony Luck <tony.luck@intel.com> | 2008-04-17 13:12:44 -0400 |
| commit | 78514c106b90b628a90c630a3bc87a6538aed865 (patch) | |
| tree | 036842b87944acc80f1e455171396b70ae0f0c29 | |
| parent | 14d0647c98f52e2d76113d44de6b771ec87a934f (diff) | |
| parent | 4cd8dc83581906948ff4cfa65007e64496b5a7c8 (diff) | |
Pull regset into release branch
| -rw-r--r-- | arch/ia64/ia32/sys_ia32.c | 649 | ||||
| -rw-r--r-- | arch/ia64/kernel/process.c | 30 | ||||
| -rw-r--r-- | arch/ia64/kernel/ptrace.c | 1217 | ||||
| -rw-r--r-- | include/asm-ia64/elf.h | 31 |
4 files changed, 1544 insertions, 383 deletions
diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index b1bf51fe97b4..7e028ceb93ba 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | #include <linux/eventpoll.h> | 38 | #include <linux/eventpoll.h> |
| 39 | #include <linux/personality.h> | 39 | #include <linux/personality.h> |
| 40 | #include <linux/ptrace.h> | 40 | #include <linux/ptrace.h> |
| 41 | #include <linux/regset.h> | ||
| 41 | #include <linux/stat.h> | 42 | #include <linux/stat.h> |
| 42 | #include <linux/ipc.h> | 43 | #include <linux/ipc.h> |
| 43 | #include <linux/capability.h> | 44 | #include <linux/capability.h> |
| @@ -2387,16 +2388,45 @@ get_free_idx (void) | |||
| 2387 | return -ESRCH; | 2388 | return -ESRCH; |
| 2388 | } | 2389 | } |
| 2389 | 2390 | ||
| 2391 | static void set_tls_desc(struct task_struct *p, int idx, | ||
| 2392 | const struct ia32_user_desc *info, int n) | ||
| 2393 | { | ||
| 2394 | struct thread_struct *t = &p->thread; | ||
| 2395 | struct desc_struct *desc = &t->tls_array[idx - GDT_ENTRY_TLS_MIN]; | ||
| 2396 | int cpu; | ||
| 2397 | |||
| 2398 | /* | ||
| 2399 | * We must not get preempted while modifying the TLS. | ||
| 2400 | */ | ||
| 2401 | cpu = get_cpu(); | ||
| 2402 | |||
| 2403 | while (n-- > 0) { | ||
| 2404 | if (LDT_empty(info)) { | ||
| 2405 | desc->a = 0; | ||
| 2406 | desc->b = 0; | ||
| 2407 | } else { | ||
| 2408 | desc->a = LDT_entry_a(info); | ||
| 2409 | desc->b = LDT_entry_b(info); | ||
| 2410 | } | ||
| 2411 | |||
| 2412 | ++info; | ||
| 2413 | ++desc; | ||
| 2414 | } | ||
| 2415 | |||
| 2416 | if (t == ¤t->thread) | ||
| 2417 | load_TLS(t, cpu); | ||
| 2418 | |||
| 2419 | put_cpu(); | ||
| 2420 | } | ||
| 2421 | |||
| 2390 | /* | 2422 | /* |
| 2391 | * Set a given TLS descriptor: | 2423 | * Set a given TLS descriptor: |
| 2392 | */ | 2424 | */ |
| 2393 | asmlinkage int | 2425 | asmlinkage int |
| 2394 | sys32_set_thread_area (struct ia32_user_desc __user *u_info) | 2426 | sys32_set_thread_area (struct ia32_user_desc __user *u_info) |
| 2395 | { | 2427 | { |
| 2396 | struct thread_struct *t = ¤t->thread; | ||
| 2397 | struct ia32_user_desc info; | 2428 | struct ia32_user_desc info; |
| 2398 | struct desc_struct *desc; | 2429 | int idx; |
| 2399 | int cpu, idx; | ||
| 2400 | 2430 | ||
| 2401 | if (copy_from_user(&info, u_info, sizeof(info))) | 2431 | if (copy_from_user(&info, u_info, sizeof(info))) |
| 2402 | return -EFAULT; | 2432 | return -EFAULT; |
| @@ -2416,18 +2446,7 @@ sys32_set_thread_area (struct ia32_user_desc __user *u_info) | |||
| 2416 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | 2446 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) |
| 2417 | return -EINVAL; | 2447 | return -EINVAL; |
| 2418 | 2448 | ||
| 2419 | desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; | 2449 | set_tls_desc(current, idx, &info, 1); |
| 2420 | |||
| 2421 | cpu = smp_processor_id(); | ||
| 2422 | |||
| 2423 | if (LDT_empty(&info)) { | ||
| 2424 | desc->a = 0; | ||
| 2425 | desc->b = 0; | ||
| 2426 | } else { | ||
| 2427 | desc->a = LDT_entry_a(&info); | ||
| 2428 | desc->b = LDT_entry_b(&info); | ||
| 2429 | } | ||
| 2430 | load_TLS(t, cpu); | ||
| 2431 | return 0; | 2450 | return 0; |
| 2432 | } | 2451 | } |
| 2433 | 2452 | ||
| @@ -2451,6 +2470,20 @@ sys32_set_thread_area (struct ia32_user_desc __user *u_info) | |||
| 2451 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) | 2470 | #define GET_PRESENT(desc) (((desc)->b >> 15) & 1) |
| 2452 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) | 2471 | #define GET_USEABLE(desc) (((desc)->b >> 20) & 1) |
| 2453 | 2472 | ||
| 2473 | static void fill_user_desc(struct ia32_user_desc *info, int idx, | ||
| 2474 | const struct desc_struct *desc) | ||
| 2475 | { | ||
| 2476 | info->entry_number = idx; | ||
| 2477 | info->base_addr = GET_BASE(desc); | ||
| 2478 | info->limit = GET_LIMIT(desc); | ||
| 2479 | info->seg_32bit = GET_32BIT(desc); | ||
| 2480 | info->contents = GET_CONTENTS(desc); | ||
| 2481 | info->read_exec_only = !GET_WRITABLE(desc); | ||
| 2482 | info->limit_in_pages = GET_LIMIT_PAGES(desc); | ||
| 2483 | info->seg_not_present = !GET_PRESENT(desc); | ||
| 2484 | info->useable = GET_USEABLE(desc); | ||
| 2485 | } | ||
| 2486 | |||
| 2454 | asmlinkage int | 2487 | asmlinkage int |
| 2455 | sys32_get_thread_area (struct ia32_user_desc __user *u_info) | 2488 | sys32_get_thread_area (struct ia32_user_desc __user *u_info) |
| 2456 | { | 2489 | { |
| @@ -2464,22 +2497,588 @@ sys32_get_thread_area (struct ia32_user_desc __user *u_info) | |||
| 2464 | return -EINVAL; | 2497 | return -EINVAL; |
| 2465 | 2498 | ||
| 2466 | desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; | 2499 | desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; |
| 2467 | 2500 | fill_user_desc(&info, idx, desc); | |
| 2468 | info.entry_number = idx; | ||
| 2469 | info.base_addr = GET_BASE(desc); | ||
| 2470 | info.limit = GET_LIMIT(desc); | ||
| 2471 | info.seg_32bit = GET_32BIT(desc); | ||
| 2472 | info.contents = GET_CONTENTS(desc); | ||
| 2473 | info.read_exec_only = !GET_WRITABLE(desc); | ||
| 2474 | info.limit_in_pages = GET_LIMIT_PAGES(desc); | ||
| 2475 | info.seg_not_present = !GET_PRESENT(desc); | ||
| 2476 | info.useable = GET_USEABLE(desc); | ||
| 2477 | 2501 | ||
| 2478 | if (copy_to_user(u_info, &info, sizeof(info))) | 2502 | if (copy_to_user(u_info, &info, sizeof(info))) |
| 2479 | return -EFAULT; | 2503 | return -EFAULT; |
| 2480 | return 0; | 2504 | return 0; |
| 2481 | } | 2505 | } |
| 2482 | 2506 | ||
| 2507 | struct regset_get { | ||
| 2508 | void *kbuf; | ||
| 2509 | void __user *ubuf; | ||
| 2510 | }; | ||
| 2511 | |||
| 2512 | struct regset_set { | ||
| 2513 | const void *kbuf; | ||
| 2514 | const void __user *ubuf; | ||
| 2515 | }; | ||
| 2516 | |||
| 2517 | struct regset_getset { | ||
| 2518 | struct task_struct *target; | ||
| 2519 | const struct user_regset *regset; | ||
| 2520 | union { | ||
| 2521 | struct regset_get get; | ||
| 2522 | struct regset_set set; | ||
| 2523 | } u; | ||
| 2524 | unsigned int pos; | ||
| 2525 | unsigned int count; | ||
| 2526 | int ret; | ||
| 2527 | }; | ||
| 2528 | |||
| 2529 | static void getfpreg(struct task_struct *task, int regno, int *val) | ||
| 2530 | { | ||
| 2531 | switch (regno / sizeof(int)) { | ||
| 2532 | case 0: | ||
| 2533 | *val = task->thread.fcr & 0xffff; | ||
| 2534 | break; | ||
| 2535 | case 1: | ||
| 2536 | *val = task->thread.fsr & 0xffff; | ||
| 2537 | break; | ||
| 2538 | case 2: | ||
| 2539 | *val = (task->thread.fsr>>16) & 0xffff; | ||
| 2540 | break; | ||
| 2541 | case 3: | ||
| 2542 | *val = task->thread.fir; | ||
| 2543 | break; | ||
| 2544 | case 4: | ||
| 2545 | *val = (task->thread.fir>>32) & 0xffff; | ||
| 2546 | break; | ||
| 2547 | case 5: | ||
| 2548 | *val = task->thread.fdr; | ||
| 2549 | break; | ||
| 2550 | case 6: | ||
| 2551 | *val = (task->thread.fdr >> 32) & 0xffff; | ||
| 2552 | break; | ||
| 2553 | } | ||
| 2554 | } | ||
| 2555 | |||
| 2556 | static void setfpreg(struct task_struct *task, int regno, int val) | ||
| 2557 | { | ||
| 2558 | switch (regno / sizeof(int)) { | ||
| 2559 | case 0: | ||
| 2560 | task->thread.fcr = (task->thread.fcr & (~0x1f3f)) | ||
| 2561 | | (val & 0x1f3f); | ||
| 2562 | break; | ||
| 2563 | case 1: | ||
| 2564 | task->thread.fsr = (task->thread.fsr & (~0xffff)) | val; | ||
| 2565 | break; | ||
| 2566 | case 2: | ||
| 2567 | task->thread.fsr = (task->thread.fsr & (~0xffff0000)) | ||
| 2568 | | (val << 16); | ||
| 2569 | break; | ||
| 2570 | case 3: | ||
| 2571 | task->thread.fir = (task->thread.fir & (~0xffffffff)) | val; | ||
| 2572 | break; | ||
| 2573 | case 5: | ||
| 2574 | task->thread.fdr = (task->thread.fdr & (~0xffffffff)) | val; | ||
| 2575 | break; | ||
| 2576 | } | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | static void access_fpreg_ia32(int regno, void *reg, | ||
| 2580 | struct pt_regs *pt, struct switch_stack *sw, | ||
| 2581 | int tos, int write) | ||
| 2582 | { | ||
| 2583 | void *f; | ||
| 2584 | |||
| 2585 | if ((regno += tos) >= 8) | ||
| 2586 | regno -= 8; | ||
| 2587 | if (regno < 4) | ||
| 2588 | f = &pt->f8 + regno; | ||
| 2589 | else if (regno <= 7) | ||
| 2590 | f = &sw->f12 + (regno - 4); | ||
| 2591 | else { | ||
| 2592 | printk(KERN_ERR "regno must be less than 7 \n"); | ||
| 2593 | return; | ||
| 2594 | } | ||
| 2595 | |||
| 2596 | if (write) | ||
| 2597 | memcpy(f, reg, sizeof(struct _fpreg_ia32)); | ||
| 2598 | else | ||
| 2599 | memcpy(reg, f, sizeof(struct _fpreg_ia32)); | ||
| 2600 | } | ||
| 2601 | |||
| 2602 | static void do_fpregs_get(struct unw_frame_info *info, void *arg) | ||
| 2603 | { | ||
| 2604 | struct regset_getset *dst = arg; | ||
| 2605 | struct task_struct *task = dst->target; | ||
| 2606 | struct pt_regs *pt; | ||
| 2607 | int start, end, tos; | ||
| 2608 | char buf[80]; | ||
| 2609 | |||
| 2610 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
| 2611 | return; | ||
| 2612 | if (dst->pos < 7 * sizeof(int)) { | ||
| 2613 | end = min((dst->pos + dst->count), | ||
| 2614 | (unsigned int)(7 * sizeof(int))); | ||
| 2615 | for (start = dst->pos; start < end; start += sizeof(int)) | ||
| 2616 | getfpreg(task, start, (int *)(buf + start)); | ||
| 2617 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2618 | &dst->u.get.kbuf, &dst->u.get.ubuf, buf, | ||
| 2619 | 0, 7 * sizeof(int)); | ||
| 2620 | if (dst->ret || dst->count == 0) | ||
| 2621 | return; | ||
| 2622 | } | ||
| 2623 | if (dst->pos < sizeof(struct ia32_user_i387_struct)) { | ||
| 2624 | pt = task_pt_regs(task); | ||
| 2625 | tos = (task->thread.fsr >> 11) & 7; | ||
| 2626 | end = min(dst->pos + dst->count, | ||
| 2627 | (unsigned int)(sizeof(struct ia32_user_i387_struct))); | ||
| 2628 | start = (dst->pos - 7 * sizeof(int)) / | ||
| 2629 | sizeof(struct _fpreg_ia32); | ||
| 2630 | end = (end - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32); | ||
| 2631 | for (; start < end; start++) | ||
| 2632 | access_fpreg_ia32(start, | ||
| 2633 | (struct _fpreg_ia32 *)buf + start, | ||
| 2634 | pt, info->sw, tos, 0); | ||
| 2635 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2636 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 2637 | buf, 7 * sizeof(int), | ||
| 2638 | sizeof(struct ia32_user_i387_struct)); | ||
| 2639 | if (dst->ret || dst->count == 0) | ||
| 2640 | return; | ||
| 2641 | } | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | static void do_fpregs_set(struct unw_frame_info *info, void *arg) | ||
| 2645 | { | ||
| 2646 | struct regset_getset *dst = arg; | ||
| 2647 | struct task_struct *task = dst->target; | ||
| 2648 | struct pt_regs *pt; | ||
| 2649 | char buf[80]; | ||
| 2650 | int end, start, tos; | ||
| 2651 | |||
| 2652 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
| 2653 | return; | ||
| 2654 | |||
| 2655 | if (dst->pos < 7 * sizeof(int)) { | ||
| 2656 | start = dst->pos; | ||
| 2657 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2658 | &dst->u.set.kbuf, &dst->u.set.ubuf, buf, | ||
| 2659 | 0, 7 * sizeof(int)); | ||
| 2660 | if (dst->ret) | ||
| 2661 | return; | ||
| 2662 | for (; start < dst->pos; start += sizeof(int)) | ||
| 2663 | setfpreg(task, start, *((int *)(buf + start))); | ||
| 2664 | if (dst->count == 0) | ||
| 2665 | return; | ||
| 2666 | } | ||
| 2667 | if (dst->pos < sizeof(struct ia32_user_i387_struct)) { | ||
| 2668 | start = (dst->pos - 7 * sizeof(int)) / | ||
| 2669 | sizeof(struct _fpreg_ia32); | ||
| 2670 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2671 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
| 2672 | buf, 7 * sizeof(int), | ||
| 2673 | sizeof(struct ia32_user_i387_struct)); | ||
| 2674 | if (dst->ret) | ||
| 2675 | return; | ||
| 2676 | pt = task_pt_regs(task); | ||
| 2677 | tos = (task->thread.fsr >> 11) & 7; | ||
| 2678 | end = (dst->pos - 7 * sizeof(int)) / sizeof(struct _fpreg_ia32); | ||
| 2679 | for (; start < end; start++) | ||
| 2680 | access_fpreg_ia32(start, | ||
| 2681 | (struct _fpreg_ia32 *)buf + start, | ||
| 2682 | pt, info->sw, tos, 1); | ||
| 2683 | if (dst->count == 0) | ||
| 2684 | return; | ||
| 2685 | } | ||
| 2686 | } | ||
| 2687 | |||
| 2688 | #define OFFSET(member) ((int)(offsetof(struct ia32_user_fxsr_struct, member))) | ||
| 2689 | static void getfpxreg(struct task_struct *task, int start, int end, char *buf) | ||
| 2690 | { | ||
| 2691 | int min_val; | ||
| 2692 | |||
| 2693 | min_val = min(end, OFFSET(fop)); | ||
| 2694 | while (start < min_val) { | ||
| 2695 | if (start == OFFSET(cwd)) | ||
| 2696 | *((short *)buf) = task->thread.fcr & 0xffff; | ||
| 2697 | else if (start == OFFSET(swd)) | ||
| 2698 | *((short *)buf) = task->thread.fsr & 0xffff; | ||
| 2699 | else if (start == OFFSET(twd)) | ||
| 2700 | *((short *)buf) = (task->thread.fsr>>16) & 0xffff; | ||
| 2701 | buf += 2; | ||
| 2702 | start += 2; | ||
| 2703 | } | ||
| 2704 | /* skip fop element */ | ||
| 2705 | if (start == OFFSET(fop)) { | ||
| 2706 | start += 2; | ||
| 2707 | buf += 2; | ||
| 2708 | } | ||
| 2709 | while (start < end) { | ||
| 2710 | if (start == OFFSET(fip)) | ||
| 2711 | *((int *)buf) = task->thread.fir; | ||
| 2712 | else if (start == OFFSET(fcs)) | ||
| 2713 | *((int *)buf) = (task->thread.fir>>32) & 0xffff; | ||
| 2714 | else if (start == OFFSET(foo)) | ||
| 2715 | *((int *)buf) = task->thread.fdr; | ||
| 2716 | else if (start == OFFSET(fos)) | ||
| 2717 | *((int *)buf) = (task->thread.fdr>>32) & 0xffff; | ||
| 2718 | else if (start == OFFSET(mxcsr)) | ||
| 2719 | *((int *)buf) = ((task->thread.fcr>>32) & 0xff80) | ||
| 2720 | | ((task->thread.fsr>>32) & 0x3f); | ||
| 2721 | buf += 4; | ||
| 2722 | start += 4; | ||
| 2723 | } | ||
| 2724 | } | ||
| 2725 | |||
| 2726 | static void setfpxreg(struct task_struct *task, int start, int end, char *buf) | ||
| 2727 | { | ||
| 2728 | int min_val, num32; | ||
| 2729 | short num; | ||
| 2730 | unsigned long num64; | ||
| 2731 | |||
| 2732 | min_val = min(end, OFFSET(fop)); | ||
| 2733 | while (start < min_val) { | ||
| 2734 | num = *((short *)buf); | ||
| 2735 | if (start == OFFSET(cwd)) { | ||
| 2736 | task->thread.fcr = (task->thread.fcr & (~0x1f3f)) | ||
| 2737 | | (num & 0x1f3f); | ||
| 2738 | } else if (start == OFFSET(swd)) { | ||
| 2739 | task->thread.fsr = (task->thread.fsr & (~0xffff)) | num; | ||
| 2740 | } else if (start == OFFSET(twd)) { | ||
| 2741 | task->thread.fsr = (task->thread.fsr & (~0xffff0000)) | ||
| 2742 | | (((int)num) << 16); | ||
| 2743 | } | ||
| 2744 | buf += 2; | ||
| 2745 | start += 2; | ||
| 2746 | } | ||
| 2747 | /* skip fop element */ | ||
| 2748 | if (start == OFFSET(fop)) { | ||
| 2749 | start += 2; | ||
| 2750 | buf += 2; | ||
| 2751 | } | ||
| 2752 | while (start < end) { | ||
| 2753 | num32 = *((int *)buf); | ||
| 2754 | if (start == OFFSET(fip)) | ||
| 2755 | task->thread.fir = (task->thread.fir & (~0xffffffff)) | ||
| 2756 | | num32; | ||
| 2757 | else if (start == OFFSET(foo)) | ||
| 2758 | task->thread.fdr = (task->thread.fdr & (~0xffffffff)) | ||
| 2759 | | num32; | ||
| 2760 | else if (start == OFFSET(mxcsr)) { | ||
| 2761 | num64 = num32 & 0xff10; | ||
| 2762 | task->thread.fcr = (task->thread.fcr & | ||
| 2763 | (~0xff1000000000UL)) | (num64<<32); | ||
| 2764 | num64 = num32 & 0x3f; | ||
| 2765 | task->thread.fsr = (task->thread.fsr & | ||
| 2766 | (~0x3f00000000UL)) | (num64<<32); | ||
| 2767 | } | ||
| 2768 | buf += 4; | ||
| 2769 | start += 4; | ||
| 2770 | } | ||
| 2771 | } | ||
| 2772 | |||
| 2773 | static void do_fpxregs_get(struct unw_frame_info *info, void *arg) | ||
| 2774 | { | ||
| 2775 | struct regset_getset *dst = arg; | ||
| 2776 | struct task_struct *task = dst->target; | ||
| 2777 | struct pt_regs *pt; | ||
| 2778 | char buf[128]; | ||
| 2779 | int start, end, tos; | ||
| 2780 | |||
| 2781 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
| 2782 | return; | ||
| 2783 | if (dst->pos < OFFSET(st_space[0])) { | ||
| 2784 | end = min(dst->pos + dst->count, (unsigned int)32); | ||
| 2785 | getfpxreg(task, dst->pos, end, buf); | ||
| 2786 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2787 | &dst->u.get.kbuf, &dst->u.get.ubuf, buf, | ||
| 2788 | 0, OFFSET(st_space[0])); | ||
| 2789 | if (dst->ret || dst->count == 0) | ||
| 2790 | return; | ||
| 2791 | } | ||
| 2792 | if (dst->pos < OFFSET(xmm_space[0])) { | ||
| 2793 | pt = task_pt_regs(task); | ||
| 2794 | tos = (task->thread.fsr >> 11) & 7; | ||
| 2795 | end = min(dst->pos + dst->count, | ||
| 2796 | (unsigned int)OFFSET(xmm_space[0])); | ||
| 2797 | start = (dst->pos - OFFSET(st_space[0])) / 16; | ||
| 2798 | end = (end - OFFSET(st_space[0])) / 16; | ||
| 2799 | for (; start < end; start++) | ||
| 2800 | access_fpreg_ia32(start, buf + 16 * start, pt, | ||
| 2801 | info->sw, tos, 0); | ||
| 2802 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2803 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 2804 | buf, OFFSET(st_space[0]), OFFSET(xmm_space[0])); | ||
| 2805 | if (dst->ret || dst->count == 0) | ||
| 2806 | return; | ||
| 2807 | } | ||
| 2808 | if (dst->pos < OFFSET(padding[0])) | ||
| 2809 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 2810 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 2811 | &info->sw->f16, OFFSET(xmm_space[0]), | ||
| 2812 | OFFSET(padding[0])); | ||
| 2813 | } | ||
| 2814 | |||
| 2815 | static void do_fpxregs_set(struct unw_frame_info *info, void *arg) | ||
| 2816 | { | ||
| 2817 | struct regset_getset *dst = arg; | ||
| 2818 | struct task_struct *task = dst->target; | ||
| 2819 | char buf[128]; | ||
| 2820 | int start, end; | ||
| 2821 | |||
| 2822 | if (dst->count == 0 || unw_unwind_to_user(info) < 0) | ||
| 2823 | return; | ||
| 2824 | |||
| 2825 | if (dst->pos < OFFSET(st_space[0])) { | ||
| 2826 | start = dst->pos; | ||
| 2827 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2828 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
| 2829 | buf, 0, OFFSET(st_space[0])); | ||
| 2830 | if (dst->ret) | ||
| 2831 | return; | ||
| 2832 | setfpxreg(task, start, dst->pos, buf); | ||
| 2833 | if (dst->count == 0) | ||
| 2834 | return; | ||
| 2835 | } | ||
| 2836 | if (dst->pos < OFFSET(xmm_space[0])) { | ||
| 2837 | struct pt_regs *pt; | ||
| 2838 | int tos; | ||
| 2839 | pt = task_pt_regs(task); | ||
| 2840 | tos = (task->thread.fsr >> 11) & 7; | ||
| 2841 | start = (dst->pos - OFFSET(st_space[0])) / 16; | ||
| 2842 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2843 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
| 2844 | buf, OFFSET(st_space[0]), OFFSET(xmm_space[0])); | ||
| 2845 | if (dst->ret) | ||
| 2846 | return; | ||
| 2847 | end = (dst->pos - OFFSET(st_space[0])) / 16; | ||
| 2848 | for (; start < end; start++) | ||
| 2849 | access_fpreg_ia32(start, buf + 16 * start, pt, info->sw, | ||
| 2850 | tos, 1); | ||
| 2851 | if (dst->count == 0) | ||
| 2852 | return; | ||
| 2853 | } | ||
| 2854 | if (dst->pos < OFFSET(padding[0])) | ||
| 2855 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 2856 | &dst->u.set.kbuf, &dst->u.set.ubuf, | ||
| 2857 | &info->sw->f16, OFFSET(xmm_space[0]), | ||
| 2858 | OFFSET(padding[0])); | ||
| 2859 | } | ||
| 2860 | #undef OFFSET | ||
| 2861 | |||
| 2862 | static int do_regset_call(void (*call)(struct unw_frame_info *, void *), | ||
| 2863 | struct task_struct *target, | ||
| 2864 | const struct user_regset *regset, | ||
| 2865 | unsigned int pos, unsigned int count, | ||
| 2866 | const void *kbuf, const void __user *ubuf) | ||
| 2867 | { | ||
| 2868 | struct regset_getset info = { .target = target, .regset = regset, | ||
| 2869 | .pos = pos, .count = count, | ||
| 2870 | .u.set = { .kbuf = kbuf, .ubuf = ubuf }, | ||
| 2871 | .ret = 0 }; | ||
| 2872 | |||
| 2873 | if (target == current) | ||
| 2874 | unw_init_running(call, &info); | ||
| 2875 | else { | ||
| 2876 | struct unw_frame_info ufi; | ||
| 2877 | memset(&ufi, 0, sizeof(ufi)); | ||
| 2878 | unw_init_from_blocked_task(&ufi, target); | ||
| 2879 | (*call)(&ufi, &info); | ||
| 2880 | } | ||
| 2881 | |||
| 2882 | return info.ret; | ||
| 2883 | } | ||
| 2884 | |||
| 2885 | static int ia32_fpregs_get(struct task_struct *target, | ||
| 2886 | const struct user_regset *regset, | ||
| 2887 | unsigned int pos, unsigned int count, | ||
| 2888 | void *kbuf, void __user *ubuf) | ||
| 2889 | { | ||
| 2890 | return do_regset_call(do_fpregs_get, target, regset, pos, count, | ||
| 2891 | kbuf, ubuf); | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | static int ia32_fpregs_set(struct task_struct *target, | ||
| 2895 | const struct user_regset *regset, | ||
| 2896 | unsigned int pos, unsigned int count, | ||
| 2897 | const void *kbuf, const void __user *ubuf) | ||
| 2898 | { | ||
| 2899 | return do_regset_call(do_fpregs_set, target, regset, pos, count, | ||
| 2900 | kbuf, ubuf); | ||
| 2901 | } | ||
| 2902 | |||
| 2903 | static int ia32_fpxregs_get(struct task_struct *target, | ||
| 2904 | const struct user_regset *regset, | ||
| 2905 | unsigned int pos, unsigned int count, | ||
| 2906 | void *kbuf, void __user *ubuf) | ||
| 2907 | { | ||
| 2908 | return do_regset_call(do_fpxregs_get, target, regset, pos, count, | ||
| 2909 | kbuf, ubuf); | ||
| 2910 | } | ||
| 2911 | |||
| 2912 | static int ia32_fpxregs_set(struct task_struct *target, | ||
| 2913 | const struct user_regset *regset, | ||
| 2914 | unsigned int pos, unsigned int count, | ||
| 2915 | const void *kbuf, const void __user *ubuf) | ||
| 2916 | { | ||
| 2917 | return do_regset_call(do_fpxregs_set, target, regset, pos, count, | ||
| 2918 | kbuf, ubuf); | ||
| 2919 | } | ||
| 2920 | |||
| 2921 | static int ia32_genregs_get(struct task_struct *target, | ||
| 2922 | const struct user_regset *regset, | ||
| 2923 | unsigned int pos, unsigned int count, | ||
| 2924 | void *kbuf, void __user *ubuf) | ||
| 2925 | { | ||
| 2926 | if (kbuf) { | ||
| 2927 | u32 *kp = kbuf; | ||
| 2928 | while (count > 0) { | ||
| 2929 | *kp++ = getreg(target, pos); | ||
| 2930 | pos += 4; | ||
| 2931 | count -= 4; | ||
| 2932 | } | ||
| 2933 | } else { | ||
| 2934 | u32 __user *up = ubuf; | ||
| 2935 | while (count > 0) { | ||
| 2936 | if (__put_user(getreg(target, pos), up++)) | ||
| 2937 | return -EFAULT; | ||
| 2938 | pos += 4; | ||
| 2939 | count -= 4; | ||
| 2940 | } | ||
| 2941 | } | ||
| 2942 | return 0; | ||
| 2943 | } | ||
| 2944 | |||
| 2945 | static int ia32_genregs_set(struct task_struct *target, | ||
| 2946 | const struct user_regset *regset, | ||
| 2947 | unsigned int pos, unsigned int count, | ||
| 2948 | const void *kbuf, const void __user *ubuf) | ||
| 2949 | { | ||
| 2950 | int ret = 0; | ||
| 2951 | |||
| 2952 | if (kbuf) { | ||
| 2953 | const u32 *kp = kbuf; | ||
| 2954 | while (!ret && count > 0) { | ||
| 2955 | putreg(target, pos, *kp++); | ||
| 2956 | pos += 4; | ||
| 2957 | count -= 4; | ||
| 2958 | } | ||
| 2959 | } else { | ||
| 2960 | const u32 __user *up = ubuf; | ||
| 2961 | u32 val; | ||
| 2962 | while (!ret && count > 0) { | ||
| 2963 | ret = __get_user(val, up++); | ||
| 2964 | if (!ret) | ||
| 2965 | putreg(target, pos, val); | ||
| 2966 | pos += 4; | ||
| 2967 | count -= 4; | ||
| 2968 | } | ||
| 2969 | } | ||
| 2970 | return ret; | ||
| 2971 | } | ||
| 2972 | |||
| 2973 | static int ia32_tls_active(struct task_struct *target, | ||
| 2974 | const struct user_regset *regset) | ||
| 2975 | { | ||
| 2976 | struct thread_struct *t = &target->thread; | ||
| 2977 | int n = GDT_ENTRY_TLS_ENTRIES; | ||
| 2978 | while (n > 0 && desc_empty(&t->tls_array[n -1])) | ||
| 2979 | --n; | ||
| 2980 | return n; | ||
| 2981 | } | ||
| 2982 | |||
| 2983 | static int ia32_tls_get(struct task_struct *target, | ||
| 2984 | const struct user_regset *regset, unsigned int pos, | ||
| 2985 | unsigned int count, void *kbuf, void __user *ubuf) | ||
| 2986 | { | ||
| 2987 | const struct desc_struct *tls; | ||
| 2988 | |||
| 2989 | if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct ia32_user_desc) || | ||
| 2990 | (pos % sizeof(struct ia32_user_desc)) != 0 || | ||
| 2991 | (count % sizeof(struct ia32_user_desc)) != 0) | ||
| 2992 | return -EINVAL; | ||
| 2993 | |||
| 2994 | pos /= sizeof(struct ia32_user_desc); | ||
| 2995 | count /= sizeof(struct ia32_user_desc); | ||
| 2996 | |||
| 2997 | tls = &target->thread.tls_array[pos]; | ||
| 2998 | |||
| 2999 | if (kbuf) { | ||
| 3000 | struct ia32_user_desc *info = kbuf; | ||
| 3001 | while (count-- > 0) | ||
| 3002 | fill_user_desc(info++, GDT_ENTRY_TLS_MIN + pos++, | ||
| 3003 | tls++); | ||
| 3004 | } else { | ||
| 3005 | struct ia32_user_desc __user *u_info = ubuf; | ||
| 3006 | while (count-- > 0) { | ||
| 3007 | struct ia32_user_desc info; | ||
| 3008 | fill_user_desc(&info, GDT_ENTRY_TLS_MIN + pos++, tls++); | ||
| 3009 | if (__copy_to_user(u_info++, &info, sizeof(info))) | ||
| 3010 | return -EFAULT; | ||
| 3011 | } | ||
| 3012 | } | ||
| 3013 | |||
| 3014 | return 0; | ||
| 3015 | } | ||
| 3016 | |||
| 3017 | static int ia32_tls_set(struct task_struct *target, | ||
| 3018 | const struct user_regset *regset, unsigned int pos, | ||
| 3019 | unsigned int count, const void *kbuf, const void __user *ubuf) | ||
| 3020 | { | ||
| 3021 | struct ia32_user_desc infobuf[GDT_ENTRY_TLS_ENTRIES]; | ||
| 3022 | const struct ia32_user_desc *info; | ||
| 3023 | |||
| 3024 | if (pos > GDT_ENTRY_TLS_ENTRIES * sizeof(struct ia32_user_desc) || | ||
| 3025 | (pos % sizeof(struct ia32_user_desc)) != 0 || | ||
| 3026 | (count % sizeof(struct ia32_user_desc)) != 0) | ||
| 3027 | return -EINVAL; | ||
| 3028 | |||
| 3029 | if (kbuf) | ||
| 3030 | info = kbuf; | ||
| 3031 | else if (__copy_from_user(infobuf, ubuf, count)) | ||
| 3032 | return -EFAULT; | ||
| 3033 | else | ||
| 3034 | info = infobuf; | ||
| 3035 | |||
| 3036 | set_tls_desc(target, | ||
| 3037 | GDT_ENTRY_TLS_MIN + (pos / sizeof(struct ia32_user_desc)), | ||
| 3038 | info, count / sizeof(struct ia32_user_desc)); | ||
| 3039 | |||
| 3040 | return 0; | ||
| 3041 | } | ||
| 3042 | |||
| 3043 | /* | ||
| 3044 | * This should match arch/i386/kernel/ptrace.c:native_regsets. | ||
| 3045 | * XXX ioperm? vm86? | ||
| 3046 | */ | ||
| 3047 | static const struct user_regset ia32_regsets[] = { | ||
| 3048 | { | ||
| 3049 | .core_note_type = NT_PRSTATUS, | ||
| 3050 | .n = sizeof(struct user_regs_struct32)/4, | ||
| 3051 | .size = 4, .align = 4, | ||
| 3052 | .get = ia32_genregs_get, .set = ia32_genregs_set | ||
| 3053 | }, | ||
| 3054 | { | ||
| 3055 | .core_note_type = NT_PRFPREG, | ||
| 3056 | .n = sizeof(struct ia32_user_i387_struct) / 4, | ||
| 3057 | .size = 4, .align = 4, | ||
| 3058 | .get = ia32_fpregs_get, .set = ia32_fpregs_set | ||
| 3059 | }, | ||
| 3060 | { | ||
| 3061 | .core_note_type = NT_PRXFPREG, | ||
| 3062 | .n = sizeof(struct ia32_user_fxsr_struct) / 4, | ||
| 3063 | .size = 4, .align = 4, | ||
| 3064 | .get = ia32_fpxregs_get, .set = ia32_fpxregs_set | ||
| 3065 | }, | ||
| 3066 | { | ||
| 3067 | .core_note_type = NT_386_TLS, | ||
| 3068 | .n = GDT_ENTRY_TLS_ENTRIES, | ||
| 3069 | .bias = GDT_ENTRY_TLS_MIN, | ||
| 3070 | .size = sizeof(struct ia32_user_desc), | ||
| 3071 | .align = sizeof(struct ia32_user_desc), | ||
| 3072 | .active = ia32_tls_active, | ||
| 3073 | .get = ia32_tls_get, .set = ia32_tls_set, | ||
| 3074 | }, | ||
| 3075 | }; | ||
| 3076 | |||
| 3077 | const struct user_regset_view user_ia32_view = { | ||
| 3078 | .name = "i386", .e_machine = EM_386, | ||
| 3079 | .regsets = ia32_regsets, .n = ARRAY_SIZE(ia32_regsets) | ||
| 3080 | }; | ||
| 3081 | |||
| 2483 | long sys32_fadvise64_64(int fd, __u32 offset_low, __u32 offset_high, | 3082 | long sys32_fadvise64_64(int fd, __u32 offset_low, __u32 offset_high, |
| 2484 | __u32 len_low, __u32 len_high, int advice) | 3083 | __u32 len_low, __u32 len_high, int advice) |
| 2485 | { | 3084 | { |
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 49937a383b23..a5ea817cbcbf 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c | |||
| @@ -625,21 +625,6 @@ do_dump_fpu (struct unw_frame_info *info, void *arg) | |||
| 625 | do_dump_task_fpu(current, info, arg); | 625 | do_dump_task_fpu(current, info, arg); |
| 626 | } | 626 | } |
| 627 | 627 | ||
| 628 | int | ||
| 629 | dump_task_regs(struct task_struct *task, elf_gregset_t *regs) | ||
| 630 | { | ||
| 631 | struct unw_frame_info tcore_info; | ||
| 632 | |||
| 633 | if (current == task) { | ||
| 634 | unw_init_running(do_copy_regs, regs); | ||
| 635 | } else { | ||
| 636 | memset(&tcore_info, 0, sizeof(tcore_info)); | ||
| 637 | unw_init_from_blocked_task(&tcore_info, task); | ||
| 638 | do_copy_task_regs(task, &tcore_info, regs); | ||
| 639 | } | ||
| 640 | return 1; | ||
| 641 | } | ||
| 642 | |||
| 643 | void | 628 | void |
| 644 | ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) | 629 | ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) |
| 645 | { | 630 | { |
| @@ -647,21 +632,6 @@ ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst) | |||
| 647 | } | 632 | } |
| 648 | 633 | ||
| 649 | int | 634 | int |
| 650 | dump_task_fpu (struct task_struct *task, elf_fpregset_t *dst) | ||
| 651 | { | ||
| 652 | struct unw_frame_info tcore_info; | ||
| 653 | |||
| 654 | if (current == task) { | ||
| 655 | unw_init_running(do_dump_fpu, dst); | ||
| 656 | } else { | ||
| 657 | memset(&tcore_info, 0, sizeof(tcore_info)); | ||
| 658 | unw_init_from_blocked_task(&tcore_info, task); | ||
| 659 | do_dump_task_fpu(task, &tcore_info, dst); | ||
| 660 | } | ||
| 661 | return 1; | ||
| 662 | } | ||
| 663 | |||
| 664 | int | ||
| 665 | dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) | 635 | dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) |
| 666 | { | 636 | { |
| 667 | unw_init_running(do_dump_fpu, dst); | 637 | unw_init_running(do_dump_fpu, dst); |
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index ab784ec4319d..2a9943b5947f 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | * | 3 | * |
| 4 | * Copyright (C) 1999-2005 Hewlett-Packard Co | 4 | * Copyright (C) 1999-2005 Hewlett-Packard Co |
| 5 | * David Mosberger-Tang <davidm@hpl.hp.com> | 5 | * David Mosberger-Tang <davidm@hpl.hp.com> |
| 6 | * Copyright (C) 2006 Intel Co | ||
| 7 | * 2006-08-12 - IA64 Native Utrace implementation support added by | ||
| 8 | * Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> | ||
| 6 | * | 9 | * |
| 7 | * Derived from the x86 and Alpha versions. | 10 | * Derived from the x86 and Alpha versions. |
| 8 | */ | 11 | */ |
| @@ -17,6 +20,8 @@ | |||
| 17 | #include <linux/security.h> | 20 | #include <linux/security.h> |
| 18 | #include <linux/audit.h> | 21 | #include <linux/audit.h> |
| 19 | #include <linux/signal.h> | 22 | #include <linux/signal.h> |
| 23 | #include <linux/regset.h> | ||
| 24 | #include <linux/elf.h> | ||
| 20 | 25 | ||
| 21 | #include <asm/pgtable.h> | 26 | #include <asm/pgtable.h> |
| 22 | #include <asm/processor.h> | 27 | #include <asm/processor.h> |
| @@ -740,25 +745,6 @@ ia64_sync_fph (struct task_struct *task) | |||
| 740 | psr->dfh = 1; | 745 | psr->dfh = 1; |
| 741 | } | 746 | } |
| 742 | 747 | ||
| 743 | static int | ||
| 744 | access_fr (struct unw_frame_info *info, int regnum, int hi, | ||
| 745 | unsigned long *data, int write_access) | ||
| 746 | { | ||
| 747 | struct ia64_fpreg fpval; | ||
| 748 | int ret; | ||
| 749 | |||
| 750 | ret = unw_get_fr(info, regnum, &fpval); | ||
| 751 | if (ret < 0) | ||
| 752 | return ret; | ||
| 753 | |||
| 754 | if (write_access) { | ||
| 755 | fpval.u.bits[hi] = *data; | ||
| 756 | ret = unw_set_fr(info, regnum, fpval); | ||
| 757 | } else | ||
| 758 | *data = fpval.u.bits[hi]; | ||
| 759 | return ret; | ||
| 760 | } | ||
| 761 | |||
| 762 | /* | 748 | /* |
| 763 | * Change the machine-state of CHILD such that it will return via the normal | 749 | * Change the machine-state of CHILD such that it will return via the normal |
| 764 | * kernel exit-path, rather than the syscall-exit path. | 750 | * kernel exit-path, rather than the syscall-exit path. |
| @@ -860,309 +846,7 @@ access_nat_bits (struct task_struct *child, struct pt_regs *pt, | |||
| 860 | 846 | ||
| 861 | static int | 847 | static int |
| 862 | access_uarea (struct task_struct *child, unsigned long addr, | 848 | access_uarea (struct task_struct *child, unsigned long addr, |
| 863 | unsigned long *data, int write_access) | 849 | unsigned long *data, int write_access); |
| 864 | { | ||
| 865 | unsigned long *ptr, regnum, urbs_end, cfm; | ||
| 866 | struct switch_stack *sw; | ||
| 867 | struct pt_regs *pt; | ||
| 868 | # define pt_reg_addr(pt, reg) ((void *) \ | ||
| 869 | ((unsigned long) (pt) \ | ||
| 870 | + offsetof(struct pt_regs, reg))) | ||
| 871 | |||
| 872 | |||
| 873 | pt = task_pt_regs(child); | ||
| 874 | sw = (struct switch_stack *) (child->thread.ksp + 16); | ||
| 875 | |||
| 876 | if ((addr & 0x7) != 0) { | ||
| 877 | dprintk("ptrace: unaligned register address 0x%lx\n", addr); | ||
| 878 | return -1; | ||
| 879 | } | ||
| 880 | |||
| 881 | if (addr < PT_F127 + 16) { | ||
| 882 | /* accessing fph */ | ||
| 883 | if (write_access) | ||
| 884 | ia64_sync_fph(child); | ||
| 885 | else | ||
| 886 | ia64_flush_fph(child); | ||
| 887 | ptr = (unsigned long *) | ||
| 888 | ((unsigned long) &child->thread.fph + addr); | ||
| 889 | } else if ((addr >= PT_F10) && (addr < PT_F11 + 16)) { | ||
| 890 | /* scratch registers untouched by kernel (saved in pt_regs) */ | ||
| 891 | ptr = pt_reg_addr(pt, f10) + (addr - PT_F10); | ||
| 892 | } else if (addr >= PT_F12 && addr < PT_F15 + 16) { | ||
| 893 | /* | ||
| 894 | * Scratch registers untouched by kernel (saved in | ||
| 895 | * switch_stack). | ||
| 896 | */ | ||
| 897 | ptr = (unsigned long *) ((long) sw | ||
| 898 | + (addr - PT_NAT_BITS - 32)); | ||
| 899 | } else if (addr < PT_AR_LC + 8) { | ||
| 900 | /* preserved state: */ | ||
| 901 | struct unw_frame_info info; | ||
| 902 | char nat = 0; | ||
| 903 | int ret; | ||
| 904 | |||
| 905 | unw_init_from_blocked_task(&info, child); | ||
| 906 | if (unw_unwind_to_user(&info) < 0) | ||
| 907 | return -1; | ||
| 908 | |||
| 909 | switch (addr) { | ||
| 910 | case PT_NAT_BITS: | ||
| 911 | return access_nat_bits(child, pt, &info, | ||
| 912 | data, write_access); | ||
| 913 | |||
| 914 | case PT_R4: case PT_R5: case PT_R6: case PT_R7: | ||
| 915 | if (write_access) { | ||
| 916 | /* read NaT bit first: */ | ||
| 917 | unsigned long dummy; | ||
| 918 | |||
| 919 | ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, | ||
| 920 | &dummy, &nat); | ||
| 921 | if (ret < 0) | ||
| 922 | return ret; | ||
| 923 | } | ||
| 924 | return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, | ||
| 925 | &nat, write_access); | ||
| 926 | |||
| 927 | case PT_B1: case PT_B2: case PT_B3: | ||
| 928 | case PT_B4: case PT_B5: | ||
| 929 | return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, | ||
| 930 | write_access); | ||
| 931 | |||
| 932 | case PT_AR_EC: | ||
| 933 | return unw_access_ar(&info, UNW_AR_EC, data, | ||
| 934 | write_access); | ||
| 935 | |||
| 936 | case PT_AR_LC: | ||
| 937 | return unw_access_ar(&info, UNW_AR_LC, data, | ||
| 938 | write_access); | ||
| 939 | |||
| 940 | default: | ||
| 941 | if (addr >= PT_F2 && addr < PT_F5 + 16) | ||
| 942 | return access_fr(&info, (addr - PT_F2)/16 + 2, | ||
| 943 | (addr & 8) != 0, data, | ||
| 944 | write_access); | ||
| 945 | else if (addr >= PT_F16 && addr < PT_F31 + 16) | ||
| 946 | return access_fr(&info, | ||
| 947 | (addr - PT_F16)/16 + 16, | ||
| 948 | (addr & 8) != 0, | ||
| 949 | data, write_access); | ||
| 950 | else { | ||
| 951 | dprintk("ptrace: rejecting access to register " | ||
| 952 | "address 0x%lx\n", addr); | ||
| 953 | return -1; | ||
| 954 | } | ||
| 955 | } | ||
| 956 | } else if (addr < PT_F9+16) { | ||
| 957 | /* scratch state */ | ||
| 958 | switch (addr) { | ||
| 959 | case PT_AR_BSP: | ||
| 960 | /* | ||
| 961 | * By convention, we use PT_AR_BSP to refer to | ||
| 962 | * the end of the user-level backing store. | ||
| 963 | * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) | ||
| 964 | * to get the real value of ar.bsp at the time | ||
| 965 | * the kernel was entered. | ||
| 966 | * | ||
| 967 | * Furthermore, when changing the contents of | ||
| 968 | * PT_AR_BSP (or PT_CFM) while the task is | ||
| 969 | * blocked in a system call, convert the state | ||
| 970 | * so that the non-system-call exit | ||
| 971 | * path is used. This ensures that the proper | ||
| 972 | * state will be picked up when resuming | ||
| 973 | * execution. However, it *also* means that | ||
| 974 | * once we write PT_AR_BSP/PT_CFM, it won't be | ||
| 975 | * possible to modify the syscall arguments of | ||
| 976 | * the pending system call any longer. This | ||
| 977 | * shouldn't be an issue because modifying | ||
| 978 | * PT_AR_BSP/PT_CFM generally implies that | ||
| 979 | * we're either abandoning the pending system | ||
| 980 | * call or that we defer it's re-execution | ||
| 981 | * (e.g., due to GDB doing an inferior | ||
| 982 | * function call). | ||
| 983 | */ | ||
| 984 | urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); | ||
| 985 | if (write_access) { | ||
| 986 | if (*data != urbs_end) { | ||
| 987 | if (in_syscall(pt)) | ||
| 988 | convert_to_non_syscall(child, | ||
| 989 | pt, | ||
| 990 | cfm); | ||
| 991 | /* | ||
| 992 | * Simulate user-level write | ||
| 993 | * of ar.bsp: | ||
| 994 | */ | ||
| 995 | pt->loadrs = 0; | ||
| 996 | pt->ar_bspstore = *data; | ||
| 997 | } | ||
| 998 | } else | ||
| 999 | *data = urbs_end; | ||
| 1000 | return 0; | ||
| 1001 | |||
| 1002 | case PT_CFM: | ||
| 1003 | urbs_end = ia64_get_user_rbs_end(child, pt, &cfm); | ||
| 1004 | if (write_access) { | ||
| 1005 | if (((cfm ^ *data) & PFM_MASK) != 0) { | ||
| 1006 | if (in_syscall(pt)) | ||
| 1007 | convert_to_non_syscall(child, | ||
| 1008 | pt, | ||
| 1009 | cfm); | ||
| 1010 | pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | ||
| 1011 | | (*data & PFM_MASK)); | ||
| 1012 | } | ||
| 1013 | } else | ||
| 1014 | *data = cfm; | ||
| 1015 | return 0; | ||
| 1016 | |||
| 1017 | case PT_CR_IPSR: | ||
| 1018 | if (write_access) { | ||
| 1019 | unsigned long tmp = *data; | ||
| 1020 | /* psr.ri==3 is a reserved value: SDM 2:25 */ | ||
| 1021 | if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) | ||
| 1022 | tmp &= ~IA64_PSR_RI; | ||
| 1023 | pt->cr_ipsr = ((tmp & IPSR_MASK) | ||
| 1024 | | (pt->cr_ipsr & ~IPSR_MASK)); | ||
| 1025 | } else | ||
| 1026 | *data = (pt->cr_ipsr & IPSR_MASK); | ||
| 1027 | return 0; | ||
| 1028 | |||
| 1029 | case PT_AR_RSC: | ||
| 1030 | if (write_access) | ||
| 1031 | pt->ar_rsc = *data | (3 << 2); /* force PL3 */ | ||
| 1032 | else | ||
| 1033 | *data = pt->ar_rsc; | ||
| 1034 | return 0; | ||
| 1035 | |||
| 1036 | case PT_AR_RNAT: | ||
| 1037 | ptr = pt_reg_addr(pt, ar_rnat); | ||
| 1038 | break; | ||
| 1039 | case PT_R1: | ||
| 1040 | ptr = pt_reg_addr(pt, r1); | ||
| 1041 | break; | ||
| 1042 | case PT_R2: case PT_R3: | ||
| 1043 | ptr = pt_reg_addr(pt, r2) + (addr - PT_R2); | ||
| 1044 | break; | ||
| 1045 | case PT_R8: case PT_R9: case PT_R10: case PT_R11: | ||
| 1046 | ptr = pt_reg_addr(pt, r8) + (addr - PT_R8); | ||
| 1047 | break; | ||
| 1048 | case PT_R12: case PT_R13: | ||
| 1049 | ptr = pt_reg_addr(pt, r12) + (addr - PT_R12); | ||
| 1050 | break; | ||
| 1051 | case PT_R14: | ||
| 1052 | ptr = pt_reg_addr(pt, r14); | ||
| 1053 | break; | ||
| 1054 | case PT_R15: | ||
| 1055 | ptr = pt_reg_addr(pt, r15); | ||
| 1056 | break; | ||
| 1057 | case PT_R16: case PT_R17: case PT_R18: case PT_R19: | ||
| 1058 | case PT_R20: case PT_R21: case PT_R22: case PT_R23: | ||
| 1059 | case PT_R24: case PT_R25: case PT_R26: case PT_R27: | ||
| 1060 | case PT_R28: case PT_R29: case PT_R30: case PT_R31: | ||
| 1061 | ptr = pt_reg_addr(pt, r16) + (addr - PT_R16); | ||
| 1062 | break; | ||
| 1063 | case PT_B0: | ||
| 1064 | ptr = pt_reg_addr(pt, b0); | ||
| 1065 | break; | ||
| 1066 | case PT_B6: | ||
| 1067 | ptr = pt_reg_addr(pt, b6); | ||
| 1068 | break; | ||
| 1069 | case PT_B7: | ||
| 1070 | ptr = pt_reg_addr(pt, b7); | ||
| 1071 | break; | ||
| 1072 | case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8: | ||
| 1073 | case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8: | ||
| 1074 | ptr = pt_reg_addr(pt, f6) + (addr - PT_F6); | ||
| 1075 | break; | ||
| 1076 | case PT_AR_BSPSTORE: | ||
| 1077 | ptr = pt_reg_addr(pt, ar_bspstore); | ||
| 1078 | break; | ||
| 1079 | case PT_AR_UNAT: | ||
| 1080 | ptr = pt_reg_addr(pt, ar_unat); | ||
| 1081 | break; | ||
| 1082 | case PT_AR_PFS: | ||
| 1083 | ptr = pt_reg_addr(pt, ar_pfs); | ||
| 1084 | break; | ||
| 1085 | case PT_AR_CCV: | ||
| 1086 | ptr = pt_reg_addr(pt, ar_ccv); | ||
| 1087 | break; | ||
| 1088 | case PT_AR_FPSR: | ||
| 1089 | ptr = pt_reg_addr(pt, ar_fpsr); | ||
| 1090 | break; | ||
| 1091 | case PT_CR_IIP: | ||
| 1092 | ptr = pt_reg_addr(pt, cr_iip); | ||
| 1093 | break; | ||
| 1094 | case PT_PR: | ||
| 1095 | ptr = pt_reg_addr(pt, pr); | ||
| 1096 | break; | ||
| 1097 | /* scratch register */ | ||
| 1098 | |||
| 1099 | default: | ||
| 1100 | /* disallow accessing anything else... */ | ||
| 1101 | dprintk("ptrace: rejecting access to register " | ||
| 1102 | "address 0x%lx\n", addr); | ||
| 1103 | return -1; | ||
| 1104 | } | ||
| 1105 | } else if (addr <= PT_AR_SSD) { | ||
| 1106 | ptr = pt_reg_addr(pt, ar_csd) + (addr - PT_AR_CSD); | ||
| 1107 | } else { | ||
| 1108 | /* access debug registers */ | ||
| 1109 | |||
| 1110 | if (addr >= PT_IBR) { | ||
| 1111 | regnum = (addr - PT_IBR) >> 3; | ||
| 1112 | ptr = &child->thread.ibr[0]; | ||
| 1113 | } else { | ||
| 1114 | regnum = (addr - PT_DBR) >> 3; | ||
| 1115 | ptr = &child->thread.dbr[0]; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | if (regnum >= 8) { | ||
| 1119 | dprintk("ptrace: rejecting access to register " | ||
| 1120 | "address 0x%lx\n", addr); | ||
| 1121 | return -1; | ||
| 1122 | } | ||
| 1123 | #ifdef CONFIG_PERFMON | ||
| 1124 | /* | ||
| 1125 | * Check if debug registers are used by perfmon. This | ||
| 1126 | * test must be done once we know that we can do the | ||
| 1127 | * operation, i.e. the arguments are all valid, but | ||
| 1128 | * before we start modifying the state. | ||
| 1129 | * | ||
| 1130 | * Perfmon needs to keep a count of how many processes | ||
| 1131 | * are trying to modify the debug registers for system | ||
| 1132 | * wide monitoring sessions. | ||
| 1133 | * | ||
| 1134 | * We also include read access here, because they may | ||
| 1135 | * cause the PMU-installed debug register state | ||
| 1136 | * (dbr[], ibr[]) to be reset. The two arrays are also | ||
| 1137 | * used by perfmon, but we do not use | ||
| 1138 | * IA64_THREAD_DBG_VALID. The registers are restored | ||
| 1139 | * by the PMU context switch code. | ||
| 1140 | */ | ||
| 1141 | if (pfm_use_debug_registers(child)) return -1; | ||
| 1142 | #endif | ||
| 1143 | |||
| 1144 | if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { | ||
| 1145 | child->thread.flags |= IA64_THREAD_DBG_VALID; | ||
| 1146 | memset(child->thread.dbr, 0, | ||
| 1147 | sizeof(child->thread.dbr)); | ||
| 1148 | memset(child->thread.ibr, 0, | ||
| 1149 | sizeof(child->thread.ibr)); | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | ptr += regnum; | ||
| 1153 | |||
| 1154 | if ((regnum & 1) && write_access) { | ||
| 1155 | /* don't let the user set kernel-level breakpoints: */ | ||
| 1156 | *ptr = *data & ~(7UL << 56); | ||
| 1157 | return 0; | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | if (write_access) | ||
| 1161 | *ptr = *data; | ||
| 1162 | else | ||
| 1163 | *data = *ptr; | ||
| 1164 | return 0; | ||
| 1165 | } | ||
| 1166 | 850 | ||
| 1167 | static long | 851 | static long |
| 1168 | ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) | 852 | ptrace_getregs (struct task_struct *child, struct pt_all_user_regs __user *ppr) |
| @@ -1626,3 +1310,892 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, | |||
| 1626 | if (test_thread_flag(TIF_RESTORE_RSE)) | 1310 | if (test_thread_flag(TIF_RESTORE_RSE)) |
| 1627 | ia64_sync_krbs(); | 1311 | ia64_sync_krbs(); |
| 1628 | } | 1312 | } |
| 1313 | |||
| 1314 | /* Utrace implementation starts here */ | ||
| 1315 | struct regset_get { | ||
| 1316 | void *kbuf; | ||
| 1317 | void __user *ubuf; | ||
| 1318 | }; | ||
| 1319 | |||
| 1320 | struct regset_set { | ||
| 1321 | const void *kbuf; | ||
| 1322 | const void __user *ubuf; | ||
| 1323 | }; | ||
| 1324 | |||
| 1325 | struct regset_getset { | ||
| 1326 | struct task_struct *target; | ||
| 1327 | const struct user_regset *regset; | ||
| 1328 | union { | ||
| 1329 | struct regset_get get; | ||
| 1330 | struct regset_set set; | ||
| 1331 | } u; | ||
| 1332 | unsigned int pos; | ||
| 1333 | unsigned int count; | ||
| 1334 | int ret; | ||
| 1335 | }; | ||
| 1336 | |||
| 1337 | static int | ||
| 1338 | access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1339 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1340 | { | ||
| 1341 | struct pt_regs *pt; | ||
| 1342 | unsigned long *ptr = NULL; | ||
| 1343 | int ret; | ||
| 1344 | char nat = 0; | ||
| 1345 | |||
| 1346 | pt = task_pt_regs(target); | ||
| 1347 | switch (addr) { | ||
| 1348 | case ELF_GR_OFFSET(1): | ||
| 1349 | ptr = &pt->r1; | ||
| 1350 | break; | ||
| 1351 | case ELF_GR_OFFSET(2): | ||
| 1352 | case ELF_GR_OFFSET(3): | ||
| 1353 | ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); | ||
| 1354 | break; | ||
| 1355 | case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): | ||
| 1356 | if (write_access) { | ||
| 1357 | /* read NaT bit first: */ | ||
| 1358 | unsigned long dummy; | ||
| 1359 | |||
| 1360 | ret = unw_get_gr(info, addr/8, &dummy, &nat); | ||
| 1361 | if (ret < 0) | ||
| 1362 | return ret; | ||
| 1363 | } | ||
| 1364 | return unw_access_gr(info, addr/8, data, &nat, write_access); | ||
| 1365 | case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): | ||
| 1366 | ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); | ||
| 1367 | break; | ||
| 1368 | case ELF_GR_OFFSET(12): | ||
| 1369 | case ELF_GR_OFFSET(13): | ||
| 1370 | ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); | ||
| 1371 | break; | ||
| 1372 | case ELF_GR_OFFSET(14): | ||
| 1373 | ptr = &pt->r14; | ||
| 1374 | break; | ||
| 1375 | case ELF_GR_OFFSET(15): | ||
| 1376 | ptr = &pt->r15; | ||
| 1377 | } | ||
| 1378 | if (write_access) | ||
| 1379 | *ptr = *data; | ||
| 1380 | else | ||
| 1381 | *data = *ptr; | ||
| 1382 | return 0; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | static int | ||
| 1386 | access_elf_breg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1387 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1388 | { | ||
| 1389 | struct pt_regs *pt; | ||
| 1390 | unsigned long *ptr = NULL; | ||
| 1391 | |||
| 1392 | pt = task_pt_regs(target); | ||
| 1393 | switch (addr) { | ||
| 1394 | case ELF_BR_OFFSET(0): | ||
| 1395 | ptr = &pt->b0; | ||
| 1396 | break; | ||
| 1397 | case ELF_BR_OFFSET(1) ... ELF_BR_OFFSET(5): | ||
| 1398 | return unw_access_br(info, (addr - ELF_BR_OFFSET(0))/8, | ||
| 1399 | data, write_access); | ||
| 1400 | case ELF_BR_OFFSET(6): | ||
| 1401 | ptr = &pt->b6; | ||
| 1402 | break; | ||
| 1403 | case ELF_BR_OFFSET(7): | ||
| 1404 | ptr = &pt->b7; | ||
| 1405 | } | ||
| 1406 | if (write_access) | ||
| 1407 | *ptr = *data; | ||
| 1408 | else | ||
| 1409 | *data = *ptr; | ||
| 1410 | return 0; | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | static int | ||
| 1414 | access_elf_areg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1415 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1416 | { | ||
| 1417 | struct pt_regs *pt; | ||
| 1418 | unsigned long cfm, urbs_end; | ||
| 1419 | unsigned long *ptr = NULL; | ||
| 1420 | |||
| 1421 | pt = task_pt_regs(target); | ||
| 1422 | if (addr >= ELF_AR_RSC_OFFSET && addr <= ELF_AR_SSD_OFFSET) { | ||
| 1423 | switch (addr) { | ||
| 1424 | case ELF_AR_RSC_OFFSET: | ||
| 1425 | /* force PL3 */ | ||
| 1426 | if (write_access) | ||
| 1427 | pt->ar_rsc = *data | (3 << 2); | ||
| 1428 | else | ||
| 1429 | *data = pt->ar_rsc; | ||
| 1430 | return 0; | ||
| 1431 | case ELF_AR_BSP_OFFSET: | ||
| 1432 | /* | ||
| 1433 | * By convention, we use PT_AR_BSP to refer to | ||
| 1434 | * the end of the user-level backing store. | ||
| 1435 | * Use ia64_rse_skip_regs(PT_AR_BSP, -CFM.sof) | ||
| 1436 | * to get the real value of ar.bsp at the time | ||
| 1437 | * the kernel was entered. | ||
| 1438 | * | ||
| 1439 | * Furthermore, when changing the contents of | ||
| 1440 | * PT_AR_BSP (or PT_CFM) while the task is | ||
| 1441 | * blocked in a system call, convert the state | ||
| 1442 | * so that the non-system-call exit | ||
| 1443 | * path is used. This ensures that the proper | ||
| 1444 | * state will be picked up when resuming | ||
| 1445 | * execution. However, it *also* means that | ||
| 1446 | * once we write PT_AR_BSP/PT_CFM, it won't be | ||
| 1447 | * possible to modify the syscall arguments of | ||
| 1448 | * the pending system call any longer. This | ||
| 1449 | * shouldn't be an issue because modifying | ||
| 1450 | * PT_AR_BSP/PT_CFM generally implies that | ||
| 1451 | * we're either abandoning the pending system | ||
| 1452 | * call or that we defer it's re-execution | ||
| 1453 | * (e.g., due to GDB doing an inferior | ||
| 1454 | * function call). | ||
| 1455 | */ | ||
| 1456 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
| 1457 | if (write_access) { | ||
| 1458 | if (*data != urbs_end) { | ||
| 1459 | if (in_syscall(pt)) | ||
| 1460 | convert_to_non_syscall(target, | ||
| 1461 | pt, | ||
| 1462 | cfm); | ||
| 1463 | /* | ||
| 1464 | * Simulate user-level write | ||
| 1465 | * of ar.bsp: | ||
| 1466 | */ | ||
| 1467 | pt->loadrs = 0; | ||
| 1468 | pt->ar_bspstore = *data; | ||
| 1469 | } | ||
| 1470 | } else | ||
| 1471 | *data = urbs_end; | ||
| 1472 | return 0; | ||
| 1473 | case ELF_AR_BSPSTORE_OFFSET: | ||
| 1474 | ptr = &pt->ar_bspstore; | ||
| 1475 | break; | ||
| 1476 | case ELF_AR_RNAT_OFFSET: | ||
| 1477 | ptr = &pt->ar_rnat; | ||
| 1478 | break; | ||
| 1479 | case ELF_AR_CCV_OFFSET: | ||
| 1480 | ptr = &pt->ar_ccv; | ||
| 1481 | break; | ||
| 1482 | case ELF_AR_UNAT_OFFSET: | ||
| 1483 | ptr = &pt->ar_unat; | ||
| 1484 | break; | ||
| 1485 | case ELF_AR_FPSR_OFFSET: | ||
| 1486 | ptr = &pt->ar_fpsr; | ||
| 1487 | break; | ||
| 1488 | case ELF_AR_PFS_OFFSET: | ||
| 1489 | ptr = &pt->ar_pfs; | ||
| 1490 | break; | ||
| 1491 | case ELF_AR_LC_OFFSET: | ||
| 1492 | return unw_access_ar(info, UNW_AR_LC, data, | ||
| 1493 | write_access); | ||
| 1494 | case ELF_AR_EC_OFFSET: | ||
| 1495 | return unw_access_ar(info, UNW_AR_EC, data, | ||
| 1496 | write_access); | ||
| 1497 | case ELF_AR_CSD_OFFSET: | ||
| 1498 | ptr = &pt->ar_csd; | ||
| 1499 | break; | ||
| 1500 | case ELF_AR_SSD_OFFSET: | ||
| 1501 | ptr = &pt->ar_ssd; | ||
| 1502 | } | ||
| 1503 | } else if (addr >= ELF_CR_IIP_OFFSET && addr <= ELF_CR_IPSR_OFFSET) { | ||
| 1504 | switch (addr) { | ||
| 1505 | case ELF_CR_IIP_OFFSET: | ||
| 1506 | ptr = &pt->cr_iip; | ||
| 1507 | break; | ||
| 1508 | case ELF_CFM_OFFSET: | ||
| 1509 | urbs_end = ia64_get_user_rbs_end(target, pt, &cfm); | ||
| 1510 | if (write_access) { | ||
| 1511 | if (((cfm ^ *data) & PFM_MASK) != 0) { | ||
| 1512 | if (in_syscall(pt)) | ||
| 1513 | convert_to_non_syscall(target, | ||
| 1514 | pt, | ||
| 1515 | cfm); | ||
| 1516 | pt->cr_ifs = ((pt->cr_ifs & ~PFM_MASK) | ||
| 1517 | | (*data & PFM_MASK)); | ||
| 1518 | } | ||
| 1519 | } else | ||
| 1520 | *data = cfm; | ||
| 1521 | return 0; | ||
| 1522 | case ELF_CR_IPSR_OFFSET: | ||
| 1523 | if (write_access) { | ||
| 1524 | unsigned long tmp = *data; | ||
| 1525 | /* psr.ri==3 is a reserved value: SDM 2:25 */ | ||
| 1526 | if ((tmp & IA64_PSR_RI) == IA64_PSR_RI) | ||
| 1527 | tmp &= ~IA64_PSR_RI; | ||
| 1528 | pt->cr_ipsr = ((tmp & IPSR_MASK) | ||
| 1529 | | (pt->cr_ipsr & ~IPSR_MASK)); | ||
| 1530 | } else | ||
| 1531 | *data = (pt->cr_ipsr & IPSR_MASK); | ||
| 1532 | return 0; | ||
| 1533 | } | ||
| 1534 | } else if (addr == ELF_NAT_OFFSET) | ||
| 1535 | return access_nat_bits(target, pt, info, | ||
| 1536 | data, write_access); | ||
| 1537 | else if (addr == ELF_PR_OFFSET) | ||
| 1538 | ptr = &pt->pr; | ||
| 1539 | else | ||
| 1540 | return -1; | ||
| 1541 | |||
| 1542 | if (write_access) | ||
| 1543 | *ptr = *data; | ||
| 1544 | else | ||
| 1545 | *data = *ptr; | ||
| 1546 | |||
| 1547 | return 0; | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | static int | ||
| 1551 | access_elf_reg(struct task_struct *target, struct unw_frame_info *info, | ||
| 1552 | unsigned long addr, unsigned long *data, int write_access) | ||
| 1553 | { | ||
| 1554 | if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) | ||
| 1555 | return access_elf_gpreg(target, info, addr, data, write_access); | ||
| 1556 | else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) | ||
| 1557 | return access_elf_breg(target, info, addr, data, write_access); | ||
| 1558 | else | ||
| 1559 | return access_elf_areg(target, info, addr, data, write_access); | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | void do_gpregs_get(struct unw_frame_info *info, void *arg) | ||
| 1563 | { | ||
| 1564 | struct pt_regs *pt; | ||
| 1565 | struct regset_getset *dst = arg; | ||
| 1566 | elf_greg_t tmp[16]; | ||
| 1567 | unsigned int i, index, min_copy; | ||
| 1568 | |||
| 1569 | if (unw_unwind_to_user(info) < 0) | ||
| 1570 | return; | ||
| 1571 | |||
| 1572 | /* | ||
| 1573 | * coredump format: | ||
| 1574 | * r0-r31 | ||
| 1575 | * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT) | ||
| 1576 | * predicate registers (p0-p63) | ||
| 1577 | * b0-b7 | ||
| 1578 | * ip cfm user-mask | ||
| 1579 | * ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 1580 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec | ||
| 1581 | */ | ||
| 1582 | |||
| 1583 | |||
| 1584 | /* Skip r0 */ | ||
| 1585 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
| 1586 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
| 1587 | &dst->u.get.kbuf, | ||
| 1588 | &dst->u.get.ubuf, | ||
| 1589 | 0, ELF_GR_OFFSET(1)); | ||
| 1590 | if (dst->ret || dst->count == 0) | ||
| 1591 | return; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | /* gr1 - gr15 */ | ||
| 1595 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
| 1596 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
| 1597 | min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? | ||
| 1598 | (dst->pos + dst->count) : ELF_GR_OFFSET(16); | ||
| 1599 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1600 | index++) | ||
| 1601 | if (access_elf_reg(dst->target, info, i, | ||
| 1602 | &tmp[index], 0) < 0) { | ||
| 1603 | dst->ret = -EIO; | ||
| 1604 | return; | ||
| 1605 | } | ||
| 1606 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1607 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1608 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
| 1609 | if (dst->ret || dst->count == 0) | ||
| 1610 | return; | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | /* r16-r31 */ | ||
| 1614 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
| 1615 | pt = task_pt_regs(dst->target); | ||
| 1616 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1617 | &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, | ||
| 1618 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
| 1619 | if (dst->ret || dst->count == 0) | ||
| 1620 | return; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | /* nat, pr, b0 - b7 */ | ||
| 1624 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
| 1625 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
| 1626 | min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? | ||
| 1627 | (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; | ||
| 1628 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1629 | index++) | ||
| 1630 | if (access_elf_reg(dst->target, info, i, | ||
| 1631 | &tmp[index], 0) < 0) { | ||
| 1632 | dst->ret = -EIO; | ||
| 1633 | return; | ||
| 1634 | } | ||
| 1635 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1636 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1637 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
| 1638 | if (dst->ret || dst->count == 0) | ||
| 1639 | return; | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 1643 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
| 1644 | */ | ||
| 1645 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
| 1646 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
| 1647 | min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? | ||
| 1648 | (dst->pos + dst->count) : ELF_AR_END_OFFSET; | ||
| 1649 | for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), | ||
| 1650 | index++) | ||
| 1651 | if (access_elf_reg(dst->target, info, i, | ||
| 1652 | &tmp[index], 0) < 0) { | ||
| 1653 | dst->ret = -EIO; | ||
| 1654 | return; | ||
| 1655 | } | ||
| 1656 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1657 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1658 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
| 1659 | } | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | void do_gpregs_set(struct unw_frame_info *info, void *arg) | ||
| 1663 | { | ||
| 1664 | struct pt_regs *pt; | ||
| 1665 | struct regset_getset *dst = arg; | ||
| 1666 | elf_greg_t tmp[16]; | ||
| 1667 | unsigned int i, index; | ||
| 1668 | |||
| 1669 | if (unw_unwind_to_user(info) < 0) | ||
| 1670 | return; | ||
| 1671 | |||
| 1672 | /* Skip r0 */ | ||
| 1673 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { | ||
| 1674 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
| 1675 | &dst->u.set.kbuf, | ||
| 1676 | &dst->u.set.ubuf, | ||
| 1677 | 0, ELF_GR_OFFSET(1)); | ||
| 1678 | if (dst->ret || dst->count == 0) | ||
| 1679 | return; | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | /* gr1-gr15 */ | ||
| 1683 | if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { | ||
| 1684 | i = dst->pos; | ||
| 1685 | index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); | ||
| 1686 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1687 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 1688 | ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); | ||
| 1689 | if (dst->ret) | ||
| 1690 | return; | ||
| 1691 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 1692 | if (access_elf_reg(dst->target, info, i, | ||
| 1693 | &tmp[index], 1) < 0) { | ||
| 1694 | dst->ret = -EIO; | ||
| 1695 | return; | ||
| 1696 | } | ||
| 1697 | if (dst->count == 0) | ||
| 1698 | return; | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | /* gr16-gr31 */ | ||
| 1702 | if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { | ||
| 1703 | pt = task_pt_regs(dst->target); | ||
| 1704 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1705 | &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, | ||
| 1706 | ELF_GR_OFFSET(16), ELF_NAT_OFFSET); | ||
| 1707 | if (dst->ret || dst->count == 0) | ||
| 1708 | return; | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | /* nat, pr, b0 - b7 */ | ||
| 1712 | if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { | ||
| 1713 | i = dst->pos; | ||
| 1714 | index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); | ||
| 1715 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1716 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 1717 | ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); | ||
| 1718 | if (dst->ret) | ||
| 1719 | return; | ||
| 1720 | for (; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 1721 | if (access_elf_reg(dst->target, info, i, | ||
| 1722 | &tmp[index], 1) < 0) { | ||
| 1723 | dst->ret = -EIO; | ||
| 1724 | return; | ||
| 1725 | } | ||
| 1726 | if (dst->count == 0) | ||
| 1727 | return; | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat | ||
| 1731 | * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd | ||
| 1732 | */ | ||
| 1733 | if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { | ||
| 1734 | i = dst->pos; | ||
| 1735 | index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); | ||
| 1736 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1737 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 1738 | ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); | ||
| 1739 | if (dst->ret) | ||
| 1740 | return; | ||
| 1741 | for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) | ||
| 1742 | if (access_elf_reg(dst->target, info, i, | ||
| 1743 | &tmp[index], 1) < 0) { | ||
| 1744 | dst->ret = -EIO; | ||
| 1745 | return; | ||
| 1746 | } | ||
| 1747 | } | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | #define ELF_FP_OFFSET(i) (i * sizeof(elf_fpreg_t)) | ||
| 1751 | |||
| 1752 | void do_fpregs_get(struct unw_frame_info *info, void *arg) | ||
| 1753 | { | ||
| 1754 | struct regset_getset *dst = arg; | ||
| 1755 | struct task_struct *task = dst->target; | ||
| 1756 | elf_fpreg_t tmp[30]; | ||
| 1757 | int index, min_copy, i; | ||
| 1758 | |||
| 1759 | if (unw_unwind_to_user(info) < 0) | ||
| 1760 | return; | ||
| 1761 | |||
| 1762 | /* Skip pos 0 and 1 */ | ||
| 1763 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
| 1764 | dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count, | ||
| 1765 | &dst->u.get.kbuf, | ||
| 1766 | &dst->u.get.ubuf, | ||
| 1767 | 0, ELF_FP_OFFSET(2)); | ||
| 1768 | if (dst->count == 0 || dst->ret) | ||
| 1769 | return; | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | /* fr2-fr31 */ | ||
| 1773 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
| 1774 | index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t); | ||
| 1775 | |||
| 1776 | min_copy = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
| 1777 | dst->pos + dst->count); | ||
| 1778 | for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t), | ||
| 1779 | index++) | ||
| 1780 | if (unw_get_fr(info, i / sizeof(elf_fpreg_t), | ||
| 1781 | &tmp[index])) { | ||
| 1782 | dst->ret = -EIO; | ||
| 1783 | return; | ||
| 1784 | } | ||
| 1785 | dst->ret = user_regset_copyout(&dst->pos, &dst->count, | ||
| 1786 | &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, | ||
| 1787 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
| 1788 | if (dst->count == 0 || dst->ret) | ||
| 1789 | return; | ||
| 1790 | } | ||
| 1791 | |||
| 1792 | /* fph */ | ||
| 1793 | if (dst->count > 0) { | ||
| 1794 | ia64_flush_fph(dst->target); | ||
| 1795 | if (task->thread.flags & IA64_THREAD_FPH_VALID) | ||
| 1796 | dst->ret = user_regset_copyout( | ||
| 1797 | &dst->pos, &dst->count, | ||
| 1798 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 1799 | &dst->target->thread.fph, | ||
| 1800 | ELF_FP_OFFSET(32), -1); | ||
| 1801 | else | ||
| 1802 | /* Zero fill instead. */ | ||
| 1803 | dst->ret = user_regset_copyout_zero( | ||
| 1804 | &dst->pos, &dst->count, | ||
| 1805 | &dst->u.get.kbuf, &dst->u.get.ubuf, | ||
| 1806 | ELF_FP_OFFSET(32), -1); | ||
| 1807 | } | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | void do_fpregs_set(struct unw_frame_info *info, void *arg) | ||
| 1811 | { | ||
| 1812 | struct regset_getset *dst = arg; | ||
| 1813 | elf_fpreg_t fpreg, tmp[30]; | ||
| 1814 | int index, start, end; | ||
| 1815 | |||
| 1816 | if (unw_unwind_to_user(info) < 0) | ||
| 1817 | return; | ||
| 1818 | |||
| 1819 | /* Skip pos 0 and 1 */ | ||
| 1820 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) { | ||
| 1821 | dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, | ||
| 1822 | &dst->u.set.kbuf, | ||
| 1823 | &dst->u.set.ubuf, | ||
| 1824 | 0, ELF_FP_OFFSET(2)); | ||
| 1825 | if (dst->count == 0 || dst->ret) | ||
| 1826 | return; | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | /* fr2-fr31 */ | ||
| 1830 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) { | ||
| 1831 | start = dst->pos; | ||
| 1832 | end = min(((unsigned int)ELF_FP_OFFSET(32)), | ||
| 1833 | dst->pos + dst->count); | ||
| 1834 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1835 | &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, | ||
| 1836 | ELF_FP_OFFSET(2), ELF_FP_OFFSET(32)); | ||
| 1837 | if (dst->ret) | ||
| 1838 | return; | ||
| 1839 | |||
| 1840 | if (start & 0xF) { /* only write high part */ | ||
| 1841 | if (unw_get_fr(info, start / sizeof(elf_fpreg_t), | ||
| 1842 | &fpreg)) { | ||
| 1843 | dst->ret = -EIO; | ||
| 1844 | return; | ||
| 1845 | } | ||
| 1846 | tmp[start / sizeof(elf_fpreg_t) - 2].u.bits[0] | ||
| 1847 | = fpreg.u.bits[0]; | ||
| 1848 | start &= ~0xFUL; | ||
| 1849 | } | ||
| 1850 | if (end & 0xF) { /* only write low part */ | ||
| 1851 | if (unw_get_fr(info, end / sizeof(elf_fpreg_t), | ||
| 1852 | &fpreg)) { | ||
| 1853 | dst->ret = -EIO; | ||
| 1854 | return; | ||
| 1855 | } | ||
| 1856 | tmp[end / sizeof(elf_fpreg_t) - 2].u.bits[1] | ||
| 1857 | = fpreg.u.bits[1]; | ||
| 1858 | end = (end + 0xF) & ~0xFUL; | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | for ( ; start < end ; start += sizeof(elf_fpreg_t)) { | ||
| 1862 | index = start / sizeof(elf_fpreg_t); | ||
| 1863 | if (unw_set_fr(info, index, tmp[index - 2])) { | ||
| 1864 | dst->ret = -EIO; | ||
| 1865 | return; | ||
| 1866 | } | ||
| 1867 | } | ||
| 1868 | if (dst->ret || dst->count == 0) | ||
| 1869 | return; | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | /* fph */ | ||
| 1873 | if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(128)) { | ||
| 1874 | ia64_sync_fph(dst->target); | ||
| 1875 | dst->ret = user_regset_copyin(&dst->pos, &dst->count, | ||
| 1876 | &dst->u.set.kbuf, | ||
| 1877 | &dst->u.set.ubuf, | ||
| 1878 | &dst->target->thread.fph, | ||
| 1879 | ELF_FP_OFFSET(32), -1); | ||
| 1880 | } | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | static int | ||
| 1884 | do_regset_call(void (*call)(struct unw_frame_info *, void *), | ||
| 1885 | struct task_struct *target, | ||
| 1886 | const struct user_regset *regset, | ||
| 1887 | unsigned int pos, unsigned int count, | ||
| 1888 | const void *kbuf, const void __user *ubuf) | ||
| 1889 | { | ||
| 1890 | struct regset_getset info = { .target = target, .regset = regset, | ||
| 1891 | .pos = pos, .count = count, | ||
| 1892 | .u.set = { .kbuf = kbuf, .ubuf = ubuf }, | ||
| 1893 | .ret = 0 }; | ||
| 1894 | |||
| 1895 | if (target == current) | ||
| 1896 | unw_init_running(call, &info); | ||
| 1897 | else { | ||
| 1898 | struct unw_frame_info ufi; | ||
| 1899 | memset(&ufi, 0, sizeof(ufi)); | ||
| 1900 | unw_init_from_blocked_task(&ufi, target); | ||
| 1901 | (*call)(&ufi, &info); | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | return info.ret; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | static int | ||
| 1908 | gpregs_get(struct task_struct *target, | ||
| 1909 | const struct user_regset *regset, | ||
| 1910 | unsigned int pos, unsigned int count, | ||
| 1911 | void *kbuf, void __user *ubuf) | ||
| 1912 | { | ||
| 1913 | return do_regset_call(do_gpregs_get, target, regset, pos, count, | ||
| 1914 | kbuf, ubuf); | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | static int gpregs_set(struct task_struct *target, | ||
| 1918 | const struct user_regset *regset, | ||
| 1919 | unsigned int pos, unsigned int count, | ||
| 1920 | const void *kbuf, const void __user *ubuf) | ||
| 1921 | { | ||
| 1922 | return do_regset_call(do_gpregs_set, target, regset, pos, count, | ||
| 1923 | kbuf, ubuf); | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | static void do_gpregs_writeback(struct unw_frame_info *info, void *arg) | ||
| 1927 | { | ||
| 1928 | do_sync_rbs(info, ia64_sync_user_rbs); | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | /* | ||
| 1932 | * This is called to write back the register backing store. | ||
| 1933 | * ptrace does this before it stops, so that a tracer reading the user | ||
| 1934 | * memory after the thread stops will get the current register data. | ||
| 1935 | */ | ||
| 1936 | static int | ||
| 1937 | gpregs_writeback(struct task_struct *target, | ||
| 1938 | const struct user_regset *regset, | ||
| 1939 | int now) | ||
| 1940 | { | ||
| 1941 | if (test_and_set_tsk_thread_flag(target, TIF_RESTORE_RSE)) | ||
| 1942 | return 0; | ||
| 1943 | tsk_set_notify_resume(target); | ||
| 1944 | return do_regset_call(do_gpregs_writeback, target, regset, 0, 0, | ||
| 1945 | NULL, NULL); | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | static int | ||
| 1949 | fpregs_active(struct task_struct *target, const struct user_regset *regset) | ||
| 1950 | { | ||
| 1951 | return (target->thread.flags & IA64_THREAD_FPH_VALID) ? 128 : 32; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | static int fpregs_get(struct task_struct *target, | ||
| 1955 | const struct user_regset *regset, | ||
| 1956 | unsigned int pos, unsigned int count, | ||
| 1957 | void *kbuf, void __user *ubuf) | ||
| 1958 | { | ||
| 1959 | return do_regset_call(do_fpregs_get, target, regset, pos, count, | ||
| 1960 | kbuf, ubuf); | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | static int fpregs_set(struct task_struct *target, | ||
| 1964 | const struct user_regset *regset, | ||
| 1965 | unsigned int pos, unsigned int count, | ||
| 1966 | const void *kbuf, const void __user *ubuf) | ||
| 1967 | { | ||
| 1968 | return do_regset_call(do_fpregs_set, target, regset, pos, count, | ||
| 1969 | kbuf, ubuf); | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | static int | ||
| 1973 | access_uarea(struct task_struct *child, unsigned long addr, | ||
| 1974 | unsigned long *data, int write_access) | ||
| 1975 | { | ||
| 1976 | unsigned int pos = -1; /* an invalid value */ | ||
| 1977 | int ret; | ||
| 1978 | unsigned long *ptr, regnum; | ||
| 1979 | |||
| 1980 | if ((addr & 0x7) != 0) { | ||
| 1981 | dprintk("ptrace: unaligned register address 0x%lx\n", addr); | ||
| 1982 | return -1; | ||
| 1983 | } | ||
| 1984 | if ((addr >= PT_NAT_BITS + 8 && addr < PT_F2) || | ||
| 1985 | (addr >= PT_R7 + 8 && addr < PT_B1) || | ||
| 1986 | (addr >= PT_AR_LC + 8 && addr < PT_CR_IPSR) || | ||
| 1987 | (addr >= PT_AR_SSD + 8 && addr < PT_DBR)) { | ||
| 1988 | dprintk("ptrace: rejecting access to register " | ||
| 1989 | "address 0x%lx\n", addr); | ||
| 1990 | return -1; | ||
| 1991 | } | ||
| 1992 | |||
| 1993 | switch (addr) { | ||
| 1994 | case PT_F32 ... (PT_F127 + 15): | ||
| 1995 | pos = addr - PT_F32 + ELF_FP_OFFSET(32); | ||
| 1996 | break; | ||
| 1997 | case PT_F2 ... (PT_F5 + 15): | ||
| 1998 | pos = addr - PT_F2 + ELF_FP_OFFSET(2); | ||
| 1999 | break; | ||
| 2000 | case PT_F10 ... (PT_F31 + 15): | ||
| 2001 | pos = addr - PT_F10 + ELF_FP_OFFSET(10); | ||
| 2002 | break; | ||
| 2003 | case PT_F6 ... (PT_F9 + 15): | ||
| 2004 | pos = addr - PT_F6 + ELF_FP_OFFSET(6); | ||
| 2005 | break; | ||
| 2006 | } | ||
| 2007 | |||
| 2008 | if (pos != -1) { | ||
| 2009 | if (write_access) | ||
| 2010 | ret = fpregs_set(child, NULL, pos, | ||
| 2011 | sizeof(unsigned long), data, NULL); | ||
| 2012 | else | ||
| 2013 | ret = fpregs_get(child, NULL, pos, | ||
| 2014 | sizeof(unsigned long), data, NULL); | ||
| 2015 | if (ret != 0) | ||
| 2016 | return -1; | ||
| 2017 | return 0; | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | switch (addr) { | ||
| 2021 | case PT_NAT_BITS: | ||
| 2022 | pos = ELF_NAT_OFFSET; | ||
| 2023 | break; | ||
| 2024 | case PT_R4 ... PT_R7: | ||
| 2025 | pos = addr - PT_R4 + ELF_GR_OFFSET(4); | ||
| 2026 | break; | ||
| 2027 | case PT_B1 ... PT_B5: | ||
| 2028 | pos = addr - PT_B1 + ELF_BR_OFFSET(1); | ||
| 2029 | break; | ||
| 2030 | case PT_AR_EC: | ||
| 2031 | pos = ELF_AR_EC_OFFSET; | ||
| 2032 | break; | ||
| 2033 | case PT_AR_LC: | ||
| 2034 | pos = ELF_AR_LC_OFFSET; | ||
| 2035 | break; | ||
| 2036 | case PT_CR_IPSR: | ||
| 2037 | pos = ELF_CR_IPSR_OFFSET; | ||
| 2038 | break; | ||
| 2039 | case PT_CR_IIP: | ||
| 2040 | pos = ELF_CR_IIP_OFFSET; | ||
| 2041 | break; | ||
| 2042 | case PT_CFM: | ||
| 2043 | pos = ELF_CFM_OFFSET; | ||
| 2044 | break; | ||
| 2045 | case PT_AR_UNAT: | ||
| 2046 | pos = ELF_AR_UNAT_OFFSET; | ||
| 2047 | break; | ||
| 2048 | case PT_AR_PFS: | ||
| 2049 | pos = ELF_AR_PFS_OFFSET; | ||
| 2050 | break; | ||
| 2051 | case PT_AR_RSC: | ||
| 2052 | pos = ELF_AR_RSC_OFFSET; | ||
| 2053 | break; | ||
| 2054 | case PT_AR_RNAT: | ||
| 2055 | pos = ELF_AR_RNAT_OFFSET; | ||
| 2056 | break; | ||
| 2057 | case PT_AR_BSPSTORE: | ||
| 2058 | pos = ELF_AR_BSPSTORE_OFFSET; | ||
| 2059 | break; | ||
| 2060 | case PT_PR: | ||
| 2061 | pos = ELF_PR_OFFSET; | ||
| 2062 | break; | ||
| 2063 | case PT_B6: | ||
| 2064 | pos = ELF_BR_OFFSET(6); | ||
| 2065 | break; | ||
| 2066 | case PT_AR_BSP: | ||
| 2067 | pos = ELF_AR_BSP_OFFSET; | ||
| 2068 | break; | ||
| 2069 | case PT_R1 ... PT_R3: | ||
| 2070 | pos = addr - PT_R1 + ELF_GR_OFFSET(1); | ||
| 2071 | break; | ||
| 2072 | case PT_R12 ... PT_R15: | ||
| 2073 | pos = addr - PT_R12 + ELF_GR_OFFSET(12); | ||
| 2074 | break; | ||
| 2075 | case PT_R8 ... PT_R11: | ||
| 2076 | pos = addr - PT_R8 + ELF_GR_OFFSET(8); | ||
| 2077 | break; | ||
| 2078 | case PT_R16 ... PT_R31: | ||
| 2079 | pos = addr - PT_R16 + ELF_GR_OFFSET(16); | ||
| 2080 | break; | ||
| 2081 | case PT_AR_CCV: | ||
| 2082 | pos = ELF_AR_CCV_OFFSET; | ||
| 2083 | break; | ||
| 2084 | case PT_AR_FPSR: | ||
| 2085 | pos = ELF_AR_FPSR_OFFSET; | ||
| 2086 | break; | ||
| 2087 | case PT_B0: | ||
| 2088 | pos = ELF_BR_OFFSET(0); | ||
| 2089 | break; | ||
| 2090 | case PT_B7: | ||
| 2091 | pos = ELF_BR_OFFSET(7); | ||
| 2092 | break; | ||
| 2093 | case PT_AR_CSD: | ||
| 2094 | pos = ELF_AR_CSD_OFFSET; | ||
| 2095 | break; | ||
| 2096 | case PT_AR_SSD: | ||
| 2097 | pos = ELF_AR_SSD_OFFSET; | ||
| 2098 | break; | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | if (pos != -1) { | ||
| 2102 | if (write_access) | ||
| 2103 | ret = gpregs_set(child, NULL, pos, | ||
| 2104 | sizeof(unsigned long), data, NULL); | ||
| 2105 | else | ||
| 2106 | ret = gpregs_get(child, NULL, pos, | ||
| 2107 | sizeof(unsigned long), data, NULL); | ||
| 2108 | if (ret != 0) | ||
| 2109 | return -1; | ||
| 2110 | return 0; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | /* access debug registers */ | ||
| 2114 | if (addr >= PT_IBR) { | ||
| 2115 | regnum = (addr - PT_IBR) >> 3; | ||
| 2116 | ptr = &child->thread.ibr[0]; | ||
| 2117 | } else { | ||
| 2118 | regnum = (addr - PT_DBR) >> 3; | ||
| 2119 | ptr = &child->thread.dbr[0]; | ||
| 2120 | } | ||
| 2121 | |||
| 2122 | if (regnum >= 8) { | ||
| 2123 | dprintk("ptrace: rejecting access to register " | ||
| 2124 | "address 0x%lx\n", addr); | ||
| 2125 | return -1; | ||
| 2126 | } | ||
| 2127 | #ifdef CONFIG_PERFMON | ||
| 2128 | /* | ||
| 2129 | * Check if debug registers are used by perfmon. This | ||
| 2130 | * test must be done once we know that we can do the | ||
| 2131 | * operation, i.e. the arguments are all valid, but | ||
| 2132 | * before we start modifying the state. | ||
| 2133 | * | ||
| 2134 | * Perfmon needs to keep a count of how many processes | ||
| 2135 | * are trying to modify the debug registers for system | ||
| 2136 | * wide monitoring sessions. | ||
| 2137 | * | ||
| 2138 | * We also include read access here, because they may | ||
| 2139 | * cause the PMU-installed debug register state | ||
| 2140 | * (dbr[], ibr[]) to be reset. The two arrays are also | ||
| 2141 | * used by perfmon, but we do not use | ||
| 2142 | * IA64_THREAD_DBG_VALID. The registers are restored | ||
| 2143 | * by the PMU context switch code. | ||
| 2144 | */ | ||
| 2145 | if (pfm_use_debug_registers(child)) | ||
| 2146 | return -1; | ||
| 2147 | #endif | ||
| 2148 | |||
| 2149 | if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) { | ||
| 2150 | child->thread.flags |= IA64_THREAD_DBG_VALID; | ||
| 2151 | memset(child->thread.dbr, 0, | ||
| 2152 | sizeof(child->thread.dbr)); | ||
| 2153 | memset(child->thread.ibr, 0, | ||
| 2154 | sizeof(child->thread.ibr)); | ||
| 2155 | } | ||
| 2156 | |||
| 2157 | ptr += regnum; | ||
| 2158 | |||
| 2159 | if ((regnum & 1) && write_access) { | ||
| 2160 | /* don't let the user set kernel-level breakpoints: */ | ||
| 2161 | *ptr = *data & ~(7UL << 56); | ||
| 2162 | return 0; | ||
| 2163 | } | ||
| 2164 | if (write_access) | ||
| 2165 | *ptr = *data; | ||
| 2166 | else | ||
| 2167 | *data = *ptr; | ||
| 2168 | return 0; | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | static const struct user_regset native_regsets[] = { | ||
| 2172 | { | ||
| 2173 | .core_note_type = NT_PRSTATUS, | ||
| 2174 | .n = ELF_NGREG, | ||
| 2175 | .size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t), | ||
| 2176 | .get = gpregs_get, .set = gpregs_set, | ||
| 2177 | .writeback = gpregs_writeback | ||
| 2178 | }, | ||
| 2179 | { | ||
| 2180 | .core_note_type = NT_PRFPREG, | ||
| 2181 | .n = ELF_NFPREG, | ||
| 2182 | .size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t), | ||
| 2183 | .get = fpregs_get, .set = fpregs_set, .active = fpregs_active | ||
| 2184 | }, | ||
| 2185 | }; | ||
| 2186 | |||
| 2187 | static const struct user_regset_view user_ia64_view = { | ||
| 2188 | .name = "ia64", | ||
| 2189 | .e_machine = EM_IA_64, | ||
| 2190 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) | ||
| 2191 | }; | ||
| 2192 | |||
| 2193 | const struct user_regset_view *task_user_regset_view(struct task_struct *tsk) | ||
| 2194 | { | ||
| 2195 | #ifdef CONFIG_IA32_SUPPORT | ||
| 2196 | extern const struct user_regset_view user_ia32_view; | ||
| 2197 | if (IS_IA32_PROCESS(task_pt_regs(tsk))) | ||
| 2198 | return &user_ia32_view; | ||
| 2199 | #endif | ||
| 2200 | return &user_ia64_view; | ||
| 2201 | } | ||
diff --git a/include/asm-ia64/elf.h b/include/asm-ia64/elf.h index f8e83eca67a2..5e0c1a6bce8d 100644 --- a/include/asm-ia64/elf.h +++ b/include/asm-ia64/elf.h | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | #define ELF_ARCH EM_IA_64 | 26 | #define ELF_ARCH EM_IA_64 |
| 27 | 27 | ||
| 28 | #define USE_ELF_CORE_DUMP | 28 | #define USE_ELF_CORE_DUMP |
| 29 | #define CORE_DUMP_USE_REGSET | ||
| 29 | 30 | ||
| 30 | /* Least-significant four bits of ELF header's e_flags are OS-specific. The bits are | 31 | /* Least-significant four bits of ELF header's e_flags are OS-specific. The bits are |
| 31 | interpreted as follows by Linux: */ | 32 | interpreted as follows by Linux: */ |
| @@ -154,6 +155,30 @@ extern void ia64_init_addr_space (void); | |||
| 154 | #define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */ | 155 | #define ELF_NGREG 128 /* we really need just 72 but let's leave some headroom... */ |
| 155 | #define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */ | 156 | #define ELF_NFPREG 128 /* f0 and f1 could be omitted, but so what... */ |
| 156 | 157 | ||
| 158 | /* elf_gregset_t register offsets */ | ||
| 159 | #define ELF_GR_0_OFFSET 0 | ||
| 160 | #define ELF_NAT_OFFSET (32 * sizeof(elf_greg_t)) | ||
| 161 | #define ELF_PR_OFFSET (33 * sizeof(elf_greg_t)) | ||
| 162 | #define ELF_BR_0_OFFSET (34 * sizeof(elf_greg_t)) | ||
| 163 | #define ELF_CR_IIP_OFFSET (42 * sizeof(elf_greg_t)) | ||
| 164 | #define ELF_CFM_OFFSET (43 * sizeof(elf_greg_t)) | ||
| 165 | #define ELF_CR_IPSR_OFFSET (44 * sizeof(elf_greg_t)) | ||
| 166 | #define ELF_GR_OFFSET(i) (ELF_GR_0_OFFSET + i * sizeof(elf_greg_t)) | ||
| 167 | #define ELF_BR_OFFSET(i) (ELF_BR_0_OFFSET + i * sizeof(elf_greg_t)) | ||
| 168 | #define ELF_AR_RSC_OFFSET (45 * sizeof(elf_greg_t)) | ||
| 169 | #define ELF_AR_BSP_OFFSET (46 * sizeof(elf_greg_t)) | ||
| 170 | #define ELF_AR_BSPSTORE_OFFSET (47 * sizeof(elf_greg_t)) | ||
| 171 | #define ELF_AR_RNAT_OFFSET (48 * sizeof(elf_greg_t)) | ||
| 172 | #define ELF_AR_CCV_OFFSET (49 * sizeof(elf_greg_t)) | ||
| 173 | #define ELF_AR_UNAT_OFFSET (50 * sizeof(elf_greg_t)) | ||
| 174 | #define ELF_AR_FPSR_OFFSET (51 * sizeof(elf_greg_t)) | ||
| 175 | #define ELF_AR_PFS_OFFSET (52 * sizeof(elf_greg_t)) | ||
| 176 | #define ELF_AR_LC_OFFSET (53 * sizeof(elf_greg_t)) | ||
| 177 | #define ELF_AR_EC_OFFSET (54 * sizeof(elf_greg_t)) | ||
| 178 | #define ELF_AR_CSD_OFFSET (55 * sizeof(elf_greg_t)) | ||
| 179 | #define ELF_AR_SSD_OFFSET (56 * sizeof(elf_greg_t)) | ||
| 180 | #define ELF_AR_END_OFFSET (57 * sizeof(elf_greg_t)) | ||
| 181 | |||
| 157 | typedef unsigned long elf_fpxregset_t; | 182 | typedef unsigned long elf_fpxregset_t; |
| 158 | 183 | ||
| 159 | typedef unsigned long elf_greg_t; | 184 | typedef unsigned long elf_greg_t; |
| @@ -183,12 +208,6 @@ extern void ia64_elf_core_copy_regs (struct pt_regs *src, elf_gregset_t dst); | |||
| 183 | 208 | ||
| 184 | struct task_struct; | 209 | struct task_struct; |
| 185 | 210 | ||
| 186 | extern int dump_task_regs(struct task_struct *, elf_gregset_t *); | ||
| 187 | extern int dump_task_fpu (struct task_struct *, elf_fpregset_t *); | ||
| 188 | |||
| 189 | #define ELF_CORE_COPY_TASK_REGS(tsk, elf_gregs) dump_task_regs(tsk, elf_gregs) | ||
| 190 | #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) | ||
| 191 | |||
| 192 | #define GATE_EHDR ((const struct elfhdr *) GATE_ADDR) | 211 | #define GATE_EHDR ((const struct elfhdr *) GATE_ADDR) |
| 193 | 212 | ||
| 194 | /* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */ | 213 | /* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */ |
