1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/kernel.h> |
3 | #include <linux/types.h> |
4 | #include <linux/thread_info.h> |
5 | #include <linux/uaccess.h> |
6 | #include <linux/sched.h> |
7 | |
8 | #include <asm/sigcontext.h> |
9 | #include <asm/fpumacro.h> |
10 | #include <asm/ptrace.h> |
11 | #include <asm/switch_to.h> |
12 | |
13 | #include "sigutil.h" |
14 | |
15 | int save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
16 | { |
17 | int err = 0; |
18 | #ifdef CONFIG_SMP |
19 | if (test_tsk_thread_flag(current, flag: TIF_USEDFPU)) { |
20 | put_psr(get_psr() | PSR_EF); |
21 | fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
22 | ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
23 | regs->psr &= ~(PSR_EF); |
24 | clear_tsk_thread_flag(current, flag: TIF_USEDFPU); |
25 | } |
26 | #else |
27 | if (current == last_task_used_math) { |
28 | put_psr(get_psr() | PSR_EF); |
29 | fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr, |
30 | ¤t->thread.fpqueue[0], ¤t->thread.fpqdepth); |
31 | last_task_used_math = NULL; |
32 | regs->psr &= ~(PSR_EF); |
33 | } |
34 | #endif |
35 | err |= __copy_to_user(to: &fpu->si_float_regs[0], |
36 | from: ¤t->thread.float_regs[0], |
37 | n: (sizeof(unsigned long) * 32)); |
38 | err |= __put_user(current->thread.fsr, &fpu->si_fsr); |
39 | err |= __put_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
40 | if (current->thread.fpqdepth != 0) |
41 | err |= __copy_to_user(to: &fpu->si_fpqueue[0], |
42 | from: ¤t->thread.fpqueue[0], |
43 | n: ((sizeof(unsigned long) + |
44 | (sizeof(unsigned long *)))*16)); |
45 | clear_used_math(); |
46 | return err; |
47 | } |
48 | |
49 | int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu) |
50 | { |
51 | int err; |
52 | |
53 | if (((unsigned long) fpu) & 3) |
54 | return -EFAULT; |
55 | |
56 | #ifdef CONFIG_SMP |
57 | if (test_tsk_thread_flag(current, flag: TIF_USEDFPU)) |
58 | regs->psr &= ~PSR_EF; |
59 | #else |
60 | if (current == last_task_used_math) { |
61 | last_task_used_math = NULL; |
62 | regs->psr &= ~PSR_EF; |
63 | } |
64 | #endif |
65 | set_used_math(); |
66 | clear_tsk_thread_flag(current, flag: TIF_USEDFPU); |
67 | |
68 | if (!access_ok(fpu, sizeof(*fpu))) |
69 | return -EFAULT; |
70 | |
71 | err = __copy_from_user(to: ¤t->thread.float_regs[0], from: &fpu->si_float_regs[0], |
72 | n: (sizeof(unsigned long) * 32)); |
73 | err |= __get_user(current->thread.fsr, &fpu->si_fsr); |
74 | err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth); |
75 | if (current->thread.fpqdepth != 0) |
76 | err |= __copy_from_user(to: ¤t->thread.fpqueue[0], |
77 | from: &fpu->si_fpqueue[0], |
78 | n: ((sizeof(unsigned long) + |
79 | (sizeof(unsigned long *)))*16)); |
80 | return err; |
81 | } |
82 | |
83 | int save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin) |
84 | { |
85 | int i, err = __put_user(wsaved, &rwin->wsaved); |
86 | |
87 | for (i = 0; i < wsaved; i++) { |
88 | struct reg_window32 *rp; |
89 | unsigned long fp; |
90 | |
91 | rp = ¤t_thread_info()->reg_window[i]; |
92 | fp = current_thread_info()->rwbuf_stkptrs[i]; |
93 | err |= copy_to_user(&rwin->reg_window[i], rp, |
94 | sizeof(struct reg_window32)); |
95 | err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]); |
96 | } |
97 | return err; |
98 | } |
99 | |
100 | int restore_rwin_state(__siginfo_rwin_t __user *rp) |
101 | { |
102 | struct thread_info *t = current_thread_info(); |
103 | int i, wsaved, err; |
104 | |
105 | if (((unsigned long) rp) & 3) |
106 | return -EFAULT; |
107 | |
108 | get_user(wsaved, &rp->wsaved); |
109 | if (wsaved > NSWINS) |
110 | return -EFAULT; |
111 | |
112 | err = 0; |
113 | for (i = 0; i < wsaved; i++) { |
114 | err |= copy_from_user(&t->reg_window[i], |
115 | &rp->reg_window[i], |
116 | sizeof(struct reg_window32)); |
117 | err |= __get_user(t->rwbuf_stkptrs[i], |
118 | &rp->rwbuf_stkptrs[i]); |
119 | } |
120 | if (err) |
121 | return err; |
122 | |
123 | t->w_saved = wsaved; |
124 | synchronize_user_stack(); |
125 | if (t->w_saved) |
126 | return -EFAULT; |
127 | return 0; |
128 | |
129 | } |
130 | |