.. | .. |
---|
18 | 18 | #include <linux/syscalls.h> |
---|
19 | 19 | #include <asm/hw_breakpoint.h> |
---|
20 | 20 | #include <linux/uaccess.h> |
---|
| 21 | +#include <asm/switch_to.h> |
---|
21 | 22 | #include <asm/unistd.h> |
---|
22 | 23 | #include <asm/debug.h> |
---|
23 | 24 | #include <asm/tm.h> |
---|
24 | 25 | |
---|
25 | 26 | #include "signal.h" |
---|
| 27 | + |
---|
| 28 | +#ifdef CONFIG_VSX |
---|
| 29 | +unsigned long copy_fpr_to_user(void __user *to, |
---|
| 30 | + struct task_struct *task) |
---|
| 31 | +{ |
---|
| 32 | + u64 buf[ELF_NFPREG]; |
---|
| 33 | + int i; |
---|
| 34 | + |
---|
| 35 | + /* save FPR copy to local buffer then write to the thread_struct */ |
---|
| 36 | + for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
---|
| 37 | + buf[i] = task->thread.TS_FPR(i); |
---|
| 38 | + buf[i] = task->thread.fp_state.fpscr; |
---|
| 39 | + return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); |
---|
| 40 | +} |
---|
| 41 | + |
---|
| 42 | +unsigned long copy_fpr_from_user(struct task_struct *task, |
---|
| 43 | + void __user *from) |
---|
| 44 | +{ |
---|
| 45 | + u64 buf[ELF_NFPREG]; |
---|
| 46 | + int i; |
---|
| 47 | + |
---|
| 48 | + if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) |
---|
| 49 | + return 1; |
---|
| 50 | + for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
---|
| 51 | + task->thread.TS_FPR(i) = buf[i]; |
---|
| 52 | + task->thread.fp_state.fpscr = buf[i]; |
---|
| 53 | + |
---|
| 54 | + return 0; |
---|
| 55 | +} |
---|
| 56 | + |
---|
| 57 | +unsigned long copy_vsx_to_user(void __user *to, |
---|
| 58 | + struct task_struct *task) |
---|
| 59 | +{ |
---|
| 60 | + u64 buf[ELF_NVSRHALFREG]; |
---|
| 61 | + int i; |
---|
| 62 | + |
---|
| 63 | + /* save FPR copy to local buffer then write to the thread_struct */ |
---|
| 64 | + for (i = 0; i < ELF_NVSRHALFREG; i++) |
---|
| 65 | + buf[i] = task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]; |
---|
| 66 | + return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); |
---|
| 67 | +} |
---|
| 68 | + |
---|
| 69 | +unsigned long copy_vsx_from_user(struct task_struct *task, |
---|
| 70 | + void __user *from) |
---|
| 71 | +{ |
---|
| 72 | + u64 buf[ELF_NVSRHALFREG]; |
---|
| 73 | + int i; |
---|
| 74 | + |
---|
| 75 | + if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) |
---|
| 76 | + return 1; |
---|
| 77 | + for (i = 0; i < ELF_NVSRHALFREG ; i++) |
---|
| 78 | + task->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; |
---|
| 79 | + return 0; |
---|
| 80 | +} |
---|
| 81 | + |
---|
| 82 | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
---|
| 83 | +unsigned long copy_ckfpr_to_user(void __user *to, |
---|
| 84 | + struct task_struct *task) |
---|
| 85 | +{ |
---|
| 86 | + u64 buf[ELF_NFPREG]; |
---|
| 87 | + int i; |
---|
| 88 | + |
---|
| 89 | + /* save FPR copy to local buffer then write to the thread_struct */ |
---|
| 90 | + for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
---|
| 91 | + buf[i] = task->thread.TS_CKFPR(i); |
---|
| 92 | + buf[i] = task->thread.ckfp_state.fpscr; |
---|
| 93 | + return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); |
---|
| 94 | +} |
---|
| 95 | + |
---|
| 96 | +unsigned long copy_ckfpr_from_user(struct task_struct *task, |
---|
| 97 | + void __user *from) |
---|
| 98 | +{ |
---|
| 99 | + u64 buf[ELF_NFPREG]; |
---|
| 100 | + int i; |
---|
| 101 | + |
---|
| 102 | + if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) |
---|
| 103 | + return 1; |
---|
| 104 | + for (i = 0; i < (ELF_NFPREG - 1) ; i++) |
---|
| 105 | + task->thread.TS_CKFPR(i) = buf[i]; |
---|
| 106 | + task->thread.ckfp_state.fpscr = buf[i]; |
---|
| 107 | + |
---|
| 108 | + return 0; |
---|
| 109 | +} |
---|
| 110 | + |
---|
| 111 | +unsigned long copy_ckvsx_to_user(void __user *to, |
---|
| 112 | + struct task_struct *task) |
---|
| 113 | +{ |
---|
| 114 | + u64 buf[ELF_NVSRHALFREG]; |
---|
| 115 | + int i; |
---|
| 116 | + |
---|
| 117 | + /* save FPR copy to local buffer then write to the thread_struct */ |
---|
| 118 | + for (i = 0; i < ELF_NVSRHALFREG; i++) |
---|
| 119 | + buf[i] = task->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET]; |
---|
| 120 | + return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); |
---|
| 121 | +} |
---|
| 122 | + |
---|
| 123 | +unsigned long copy_ckvsx_from_user(struct task_struct *task, |
---|
| 124 | + void __user *from) |
---|
| 125 | +{ |
---|
| 126 | + u64 buf[ELF_NVSRHALFREG]; |
---|
| 127 | + int i; |
---|
| 128 | + |
---|
| 129 | + if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) |
---|
| 130 | + return 1; |
---|
| 131 | + for (i = 0; i < ELF_NVSRHALFREG ; i++) |
---|
| 132 | + task->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; |
---|
| 133 | + return 0; |
---|
| 134 | +} |
---|
| 135 | +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ |
---|
| 136 | +#else |
---|
| 137 | +inline unsigned long copy_fpr_to_user(void __user *to, |
---|
| 138 | + struct task_struct *task) |
---|
| 139 | +{ |
---|
| 140 | + return __copy_to_user(to, task->thread.fp_state.fpr, |
---|
| 141 | + ELF_NFPREG * sizeof(double)); |
---|
| 142 | +} |
---|
| 143 | + |
---|
| 144 | +inline unsigned long copy_fpr_from_user(struct task_struct *task, |
---|
| 145 | + void __user *from) |
---|
| 146 | +{ |
---|
| 147 | + return __copy_from_user(task->thread.fp_state.fpr, from, |
---|
| 148 | + ELF_NFPREG * sizeof(double)); |
---|
| 149 | +} |
---|
| 150 | + |
---|
| 151 | +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
---|
| 152 | +inline unsigned long copy_ckfpr_to_user(void __user *to, |
---|
| 153 | + struct task_struct *task) |
---|
| 154 | +{ |
---|
| 155 | + return __copy_to_user(to, task->thread.ckfp_state.fpr, |
---|
| 156 | + ELF_NFPREG * sizeof(double)); |
---|
| 157 | +} |
---|
| 158 | + |
---|
| 159 | +inline unsigned long copy_ckfpr_from_user(struct task_struct *task, |
---|
| 160 | + void __user *from) |
---|
| 161 | +{ |
---|
| 162 | + return __copy_from_user(task->thread.ckfp_state.fpr, from, |
---|
| 163 | + ELF_NFPREG * sizeof(double)); |
---|
| 164 | +} |
---|
| 165 | +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ |
---|
| 166 | +#endif |
---|
26 | 167 | |
---|
27 | 168 | /* Log an error when sending an unhandled signal to a process. Controlled |
---|
28 | 169 | * through debug.exception-trace sysctl. |
---|
.. | .. |
---|
44 | 185 | newsp = (oldsp - frame_size) & ~0xFUL; |
---|
45 | 186 | |
---|
46 | 187 | /* Check access */ |
---|
47 | | - if (!access_ok(VERIFY_WRITE, (void __user *)newsp, oldsp - newsp)) |
---|
| 188 | + if (!access_ok((void __user *)newsp, oldsp - newsp)) |
---|
48 | 189 | return NULL; |
---|
49 | 190 | |
---|
50 | 191 | return (void __user *)newsp; |
---|
.. | .. |
---|
57 | 198 | int restart = 1; |
---|
58 | 199 | |
---|
59 | 200 | /* syscall ? */ |
---|
60 | | - if (TRAP(regs) != 0x0C00) |
---|
| 201 | + if (!trap_is_syscall(regs)) |
---|
| 202 | + return; |
---|
| 203 | + |
---|
| 204 | + if (trap_norestart(regs)) |
---|
61 | 205 | return; |
---|
62 | 206 | |
---|
63 | 207 | /* error signalled ? */ |
---|
64 | | - if (!(regs->ccr & 0x10000000)) |
---|
| 208 | + if (trap_is_scv(regs)) { |
---|
| 209 | + /* 32-bit compat mode sign extend? */ |
---|
| 210 | + if (!IS_ERR_VALUE(ret)) |
---|
| 211 | + return; |
---|
| 212 | + ret = -ret; |
---|
| 213 | + } else if (!(regs->ccr & 0x10000000)) { |
---|
65 | 214 | return; |
---|
| 215 | + } |
---|
66 | 216 | |
---|
67 | 217 | switch (ret) { |
---|
68 | 218 | case ERESTART_RESTARTBLOCK: |
---|
.. | .. |
---|
95 | 245 | regs->nip -= 4; |
---|
96 | 246 | regs->result = 0; |
---|
97 | 247 | } else { |
---|
98 | | - regs->result = -EINTR; |
---|
99 | | - regs->gpr[3] = EINTR; |
---|
100 | | - regs->ccr |= 0x10000000; |
---|
| 248 | + if (trap_is_scv(regs)) { |
---|
| 249 | + regs->result = -EINTR; |
---|
| 250 | + regs->gpr[3] = -EINTR; |
---|
| 251 | + } else { |
---|
| 252 | + regs->result = -EINTR; |
---|
| 253 | + regs->gpr[3] = EINTR; |
---|
| 254 | + regs->ccr |= 0x10000000; |
---|
| 255 | + } |
---|
101 | 256 | } |
---|
102 | 257 | } |
---|
103 | 258 | |
---|
.. | .. |
---|
106 | 261 | sigset_t *oldset = sigmask_to_save(); |
---|
107 | 262 | struct ksignal ksig = { .sig = 0 }; |
---|
108 | 263 | int ret; |
---|
109 | | - int is32 = is_32bit_task(); |
---|
110 | 264 | |
---|
111 | 265 | BUG_ON(tsk != current); |
---|
112 | 266 | |
---|
.. | .. |
---|
118 | 272 | if (ksig.sig <= 0) { |
---|
119 | 273 | /* No signal to deliver -- put the saved sigmask back */ |
---|
120 | 274 | restore_saved_sigmask(); |
---|
121 | | - tsk->thread.regs->trap = 0; |
---|
| 275 | + set_trap_norestart(tsk->thread.regs); |
---|
122 | 276 | return; /* no signals delivered */ |
---|
123 | 277 | } |
---|
124 | 278 | |
---|
125 | | -#ifndef CONFIG_PPC_ADV_DEBUG_REGS |
---|
126 | 279 | /* |
---|
127 | 280 | * Reenable the DABR before delivering the signal to |
---|
128 | 281 | * user space. The DABR will have been cleared if it |
---|
129 | 282 | * triggered inside the kernel. |
---|
130 | 283 | */ |
---|
131 | | - if (tsk->thread.hw_brk.address && tsk->thread.hw_brk.type) |
---|
132 | | - __set_breakpoint(&tsk->thread.hw_brk); |
---|
133 | | -#endif |
---|
| 284 | + if (!IS_ENABLED(CONFIG_PPC_ADV_DEBUG_REGS)) { |
---|
| 285 | + int i; |
---|
| 286 | + |
---|
| 287 | + for (i = 0; i < nr_wp_slots(); i++) { |
---|
| 288 | + if (tsk->thread.hw_brk[i].address && tsk->thread.hw_brk[i].type) |
---|
| 289 | + __set_breakpoint(i, &tsk->thread.hw_brk[i]); |
---|
| 290 | + } |
---|
| 291 | + } |
---|
| 292 | + |
---|
134 | 293 | /* Re-enable the breakpoints for the signal stack */ |
---|
135 | 294 | thread_change_pc(tsk, tsk->thread.regs); |
---|
136 | 295 | |
---|
137 | 296 | rseq_signal_deliver(&ksig, tsk->thread.regs); |
---|
138 | 297 | |
---|
139 | | - if (is32) { |
---|
| 298 | + if (is_32bit_task()) { |
---|
140 | 299 | if (ksig.ka.sa.sa_flags & SA_SIGINFO) |
---|
141 | 300 | ret = handle_rt_signal32(&ksig, oldset, tsk); |
---|
142 | 301 | else |
---|
.. | .. |
---|
145 | 304 | ret = handle_rt_signal64(&ksig, oldset, tsk); |
---|
146 | 305 | } |
---|
147 | 306 | |
---|
148 | | - tsk->thread.regs->trap = 0; |
---|
| 307 | + set_trap_norestart(tsk->thread.regs); |
---|
149 | 308 | signal_setup_done(ret, &ksig, test_thread_flag(TIF_SINGLESTEP)); |
---|
150 | 309 | } |
---|
151 | 310 | |
---|
.. | .. |
---|
153 | 312 | { |
---|
154 | 313 | user_exit(); |
---|
155 | 314 | |
---|
156 | | - /* Check valid addr_limit, TIF check is done there */ |
---|
157 | | - addr_limit_user_check(); |
---|
158 | | - |
---|
159 | 315 | if (thread_info_flags & _TIF_UPROBE) |
---|
160 | 316 | uprobe_notify_resume(regs); |
---|
161 | 317 | |
---|
162 | 318 | if (thread_info_flags & _TIF_PATCH_PENDING) |
---|
163 | 319 | klp_update_patch_state(current); |
---|
164 | 320 | |
---|
165 | | - if (thread_info_flags & _TIF_SIGPENDING) { |
---|
| 321 | + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) { |
---|
166 | 322 | BUG_ON(regs != current->thread.regs); |
---|
167 | 323 | do_signal(current); |
---|
168 | 324 | } |
---|
169 | 325 | |
---|
170 | 326 | if (thread_info_flags & _TIF_NOTIFY_RESUME) { |
---|
171 | | - clear_thread_flag(TIF_NOTIFY_RESUME); |
---|
172 | 327 | tracehook_notify_resume(regs); |
---|
173 | 328 | rseq_handle_notify_resume(NULL, regs); |
---|
174 | 329 | } |
---|