diff options
author | Shaohua Li <shaohua.li@intel.com> | 2008-02-28 03:09:33 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2008-03-12 19:27:03 -0400 |
commit | 75529219373e53042fc46c86d991125e616f42dd (patch) | |
tree | 72c3e11a53d3ffb4106100b8e0c154fcb21073a3 /arch/ia64/ia32 | |
parent | c70f8f68676866d778564de337bec6b8734c3850 (diff) |
[IA64] regset: 32-bit support
This is the 32-bit regset implementation under IA64. Basically register
read/write, which is derived from current ptrace register read/write.
This version added TLS support.
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch/ia64/ia32')
-rw-r--r-- | arch/ia64/ia32/sys_ia32.c | 649 |
1 files changed, 624 insertions, 25 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 | { |