1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | #include <linux/regset.h> |
4 | |
5 | #include <asm/switch_to.h> |
6 | #include <asm/tm.h> |
7 | #include <asm/asm-prototypes.h> |
8 | |
9 | #include "ptrace-decl.h" |
10 | |
11 | void flush_tmregs_to_thread(struct task_struct *tsk) |
12 | { |
13 | /* |
14 | * If task is not current, it will have been flushed already to |
15 | * it's thread_struct during __switch_to(). |
16 | * |
17 | * A reclaim flushes ALL the state or if not in TM save TM SPRs |
18 | * in the appropriate thread structures from live. |
19 | */ |
20 | |
21 | if (!cpu_has_feature(CPU_FTR_TM) || tsk != current) |
22 | return; |
23 | |
24 | if (MSR_TM_SUSPENDED(mfmsr())) { |
25 | tm_reclaim_current(TM_CAUSE_SIGNAL); |
26 | } else { |
27 | tm_enable(); |
28 | tm_save_sprs(&tsk->thread); |
29 | } |
30 | } |
31 | |
32 | static unsigned long get_user_ckpt_msr(struct task_struct *task) |
33 | { |
34 | return task->thread.ckpt_regs.msr | task->thread.fpexc_mode; |
35 | } |
36 | |
37 | static int set_user_ckpt_msr(struct task_struct *task, unsigned long msr) |
38 | { |
39 | task->thread.ckpt_regs.msr &= ~MSR_DEBUGCHANGE; |
40 | task->thread.ckpt_regs.msr |= msr & MSR_DEBUGCHANGE; |
41 | return 0; |
42 | } |
43 | |
44 | static int set_user_ckpt_trap(struct task_struct *task, unsigned long trap) |
45 | { |
46 | set_trap(&task->thread.ckpt_regs, trap); |
47 | return 0; |
48 | } |
49 | |
50 | /** |
51 | * tm_cgpr_active - get active number of registers in CGPR |
52 | * @target: The target task. |
53 | * @regset: The user regset structure. |
54 | * |
55 | * This function checks for the active number of available |
56 | * regisers in transaction checkpointed GPR category. |
57 | */ |
58 | int tm_cgpr_active(struct task_struct *target, const struct user_regset *regset) |
59 | { |
60 | if (!cpu_has_feature(CPU_FTR_TM)) |
61 | return -ENODEV; |
62 | |
63 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
64 | return 0; |
65 | |
66 | return regset->n; |
67 | } |
68 | |
69 | /** |
70 | * tm_cgpr_get - get CGPR registers |
71 | * @target: The target task. |
72 | * @regset: The user regset structure. |
73 | * @to: Destination of copy. |
74 | * |
75 | * This function gets transaction checkpointed GPR registers. |
76 | * |
77 | * When the transaction is active, 'ckpt_regs' holds all the checkpointed |
78 | * GPR register values for the current transaction to fall back on if it |
79 | * aborts in between. This function gets those checkpointed GPR registers. |
80 | * The userspace interface buffer layout is as follows. |
81 | * |
82 | * struct data { |
83 | * struct pt_regs ckpt_regs; |
84 | * }; |
85 | */ |
86 | int tm_cgpr_get(struct task_struct *target, const struct user_regset *regset, |
87 | struct membuf to) |
88 | { |
89 | struct membuf to_msr = membuf_at(&to, offsetof(struct pt_regs, msr)); |
90 | #ifdef CONFIG_PPC64 |
91 | struct membuf to_softe = membuf_at(&to, offsetof(struct pt_regs, softe)); |
92 | #endif |
93 | |
94 | if (!cpu_has_feature(CPU_FTR_TM)) |
95 | return -ENODEV; |
96 | |
97 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
98 | return -ENODATA; |
99 | |
100 | flush_tmregs_to_thread(target); |
101 | flush_fp_to_thread(target); |
102 | flush_altivec_to_thread(target); |
103 | |
104 | membuf_write(&to, &target->thread.ckpt_regs, sizeof(struct user_pt_regs)); |
105 | |
106 | membuf_store(&to_msr, get_user_ckpt_msr(target)); |
107 | #ifdef CONFIG_PPC64 |
108 | membuf_store(&to_softe, 0x1ul); |
109 | #endif |
110 | return membuf_zero(&to, ELF_NGREG * sizeof(unsigned long) - |
111 | sizeof(struct user_pt_regs)); |
112 | } |
113 | |
114 | /* |
115 | * tm_cgpr_set - set the CGPR registers |
116 | * @target: The target task. |
117 | * @regset: The user regset structure. |
118 | * @pos: The buffer position. |
119 | * @count: Number of bytes to copy. |
120 | * @kbuf: Kernel buffer to copy into. |
121 | * @ubuf: User buffer to copy from. |
122 | * |
123 | * This function sets in transaction checkpointed GPR registers. |
124 | * |
125 | * When the transaction is active, 'ckpt_regs' holds the checkpointed |
126 | * GPR register values for the current transaction to fall back on if it |
127 | * aborts in between. This function sets those checkpointed GPR registers. |
128 | * The userspace interface buffer layout is as follows. |
129 | * |
130 | * struct data { |
131 | * struct pt_regs ckpt_regs; |
132 | * }; |
133 | */ |
134 | int tm_cgpr_set(struct task_struct *target, const struct user_regset *regset, |
135 | unsigned int pos, unsigned int count, |
136 | const void *kbuf, const void __user *ubuf) |
137 | { |
138 | unsigned long reg; |
139 | int ret; |
140 | |
141 | if (!cpu_has_feature(CPU_FTR_TM)) |
142 | return -ENODEV; |
143 | |
144 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
145 | return -ENODATA; |
146 | |
147 | flush_tmregs_to_thread(target); |
148 | flush_fp_to_thread(target); |
149 | flush_altivec_to_thread(target); |
150 | |
151 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
152 | data: &target->thread.ckpt_regs, |
153 | start_pos: 0, end_pos: PT_MSR * sizeof(reg)); |
154 | |
155 | if (!ret && count > 0) { |
156 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, |
157 | PT_MSR * sizeof(reg), |
158 | (PT_MSR + 1) * sizeof(reg)); |
159 | if (!ret) |
160 | ret = set_user_ckpt_msr(task: target, msr: reg); |
161 | } |
162 | |
163 | BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != |
164 | offsetof(struct pt_regs, msr) + sizeof(long)); |
165 | |
166 | if (!ret) |
167 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
168 | &target->thread.ckpt_regs.orig_gpr3, |
169 | PT_ORIG_R3 * sizeof(reg), |
170 | (PT_MAX_PUT_REG + 1) * sizeof(reg)); |
171 | |
172 | if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) |
173 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
174 | (PT_MAX_PUT_REG + 1) * sizeof(reg), |
175 | PT_TRAP * sizeof(reg)); |
176 | |
177 | if (!ret && count > 0) { |
178 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, data: ®, |
179 | start_pos: PT_TRAP * sizeof(reg), |
180 | end_pos: (PT_TRAP + 1) * sizeof(reg)); |
181 | if (!ret) |
182 | ret = set_user_ckpt_trap(task: target, trap: reg); |
183 | } |
184 | |
185 | if (!ret) |
186 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
187 | start_pos: (PT_TRAP + 1) * sizeof(reg), end_pos: -1); |
188 | |
189 | return ret; |
190 | } |
191 | |
192 | /** |
193 | * tm_cfpr_active - get active number of registers in CFPR |
194 | * @target: The target task. |
195 | * @regset: The user regset structure. |
196 | * |
197 | * This function checks for the active number of available |
198 | * regisers in transaction checkpointed FPR category. |
199 | */ |
200 | int tm_cfpr_active(struct task_struct *target, const struct user_regset *regset) |
201 | { |
202 | if (!cpu_has_feature(CPU_FTR_TM)) |
203 | return -ENODEV; |
204 | |
205 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
206 | return 0; |
207 | |
208 | return regset->n; |
209 | } |
210 | |
211 | /** |
212 | * tm_cfpr_get - get CFPR registers |
213 | * @target: The target task. |
214 | * @regset: The user regset structure. |
215 | * @to: Destination of copy. |
216 | * |
217 | * This function gets in transaction checkpointed FPR registers. |
218 | * |
219 | * When the transaction is active 'ckfp_state' holds the checkpointed |
220 | * values for the current transaction to fall back on if it aborts |
221 | * in between. This function gets those checkpointed FPR registers. |
222 | * The userspace interface buffer layout is as follows. |
223 | * |
224 | * struct data { |
225 | * u64 fpr[32]; |
226 | * u64 fpscr; |
227 | *}; |
228 | */ |
229 | int tm_cfpr_get(struct task_struct *target, const struct user_regset *regset, |
230 | struct membuf to) |
231 | { |
232 | u64 buf[33]; |
233 | int i; |
234 | |
235 | if (!cpu_has_feature(CPU_FTR_TM)) |
236 | return -ENODEV; |
237 | |
238 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
239 | return -ENODATA; |
240 | |
241 | flush_tmregs_to_thread(target); |
242 | flush_fp_to_thread(target); |
243 | flush_altivec_to_thread(target); |
244 | |
245 | /* copy to local buffer then write that out */ |
246 | for (i = 0; i < 32 ; i++) |
247 | buf[i] = target->thread.TS_CKFPR(i); |
248 | buf[32] = target->thread.ckfp_state.fpscr; |
249 | return membuf_write(s: &to, v: buf, size: sizeof(buf)); |
250 | } |
251 | |
252 | /** |
253 | * tm_cfpr_set - set CFPR registers |
254 | * @target: The target task. |
255 | * @regset: The user regset structure. |
256 | * @pos: The buffer position. |
257 | * @count: Number of bytes to copy. |
258 | * @kbuf: Kernel buffer to copy into. |
259 | * @ubuf: User buffer to copy from. |
260 | * |
261 | * This function sets in transaction checkpointed FPR registers. |
262 | * |
263 | * When the transaction is active 'ckfp_state' holds the checkpointed |
264 | * FPR register values for the current transaction to fall back on |
265 | * if it aborts in between. This function sets these checkpointed |
266 | * FPR registers. The userspace interface buffer layout is as follows. |
267 | * |
268 | * struct data { |
269 | * u64 fpr[32]; |
270 | * u64 fpscr; |
271 | *}; |
272 | */ |
273 | int tm_cfpr_set(struct task_struct *target, const struct user_regset *regset, |
274 | unsigned int pos, unsigned int count, |
275 | const void *kbuf, const void __user *ubuf) |
276 | { |
277 | u64 buf[33]; |
278 | int i; |
279 | |
280 | if (!cpu_has_feature(CPU_FTR_TM)) |
281 | return -ENODEV; |
282 | |
283 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
284 | return -ENODATA; |
285 | |
286 | flush_tmregs_to_thread(target); |
287 | flush_fp_to_thread(target); |
288 | flush_altivec_to_thread(target); |
289 | |
290 | for (i = 0; i < 32; i++) |
291 | buf[i] = target->thread.TS_CKFPR(i); |
292 | buf[32] = target->thread.ckfp_state.fpscr; |
293 | |
294 | /* copy to local buffer then write that out */ |
295 | i = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, data: buf, start_pos: 0, end_pos: -1); |
296 | if (i) |
297 | return i; |
298 | for (i = 0; i < 32 ; i++) |
299 | target->thread.TS_CKFPR(i) = buf[i]; |
300 | target->thread.ckfp_state.fpscr = buf[32]; |
301 | return 0; |
302 | } |
303 | |
304 | /** |
305 | * tm_cvmx_active - get active number of registers in CVMX |
306 | * @target: The target task. |
307 | * @regset: The user regset structure. |
308 | * |
309 | * This function checks for the active number of available |
310 | * regisers in checkpointed VMX category. |
311 | */ |
312 | int tm_cvmx_active(struct task_struct *target, const struct user_regset *regset) |
313 | { |
314 | if (!cpu_has_feature(CPU_FTR_TM)) |
315 | return -ENODEV; |
316 | |
317 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
318 | return 0; |
319 | |
320 | return regset->n; |
321 | } |
322 | |
323 | /** |
324 | * tm_cvmx_get - get CMVX registers |
325 | * @target: The target task. |
326 | * @regset: The user regset structure. |
327 | * @to: Destination of copy. |
328 | * |
329 | * This function gets in transaction checkpointed VMX registers. |
330 | * |
331 | * When the transaction is active 'ckvr_state' and 'ckvrsave' hold |
332 | * the checkpointed values for the current transaction to fall |
333 | * back on if it aborts in between. The userspace interface buffer |
334 | * layout is as follows. |
335 | * |
336 | * struct data { |
337 | * vector128 vr[32]; |
338 | * vector128 vscr; |
339 | * vector128 vrsave; |
340 | *}; |
341 | */ |
342 | int tm_cvmx_get(struct task_struct *target, const struct user_regset *regset, |
343 | struct membuf to) |
344 | { |
345 | union { |
346 | elf_vrreg_t reg; |
347 | u32 word; |
348 | } vrsave; |
349 | BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32])); |
350 | |
351 | if (!cpu_has_feature(CPU_FTR_TM)) |
352 | return -ENODEV; |
353 | |
354 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
355 | return -ENODATA; |
356 | |
357 | /* Flush the state */ |
358 | flush_tmregs_to_thread(target); |
359 | flush_fp_to_thread(target); |
360 | flush_altivec_to_thread(target); |
361 | |
362 | membuf_write(&to, &target->thread.ckvr_state, 33 * sizeof(vector128)); |
363 | /* |
364 | * Copy out only the low-order word of vrsave. |
365 | */ |
366 | memset(&vrsave, 0, sizeof(vrsave)); |
367 | vrsave.word = target->thread.ckvrsave; |
368 | return membuf_write(s: &to, v: &vrsave, size: sizeof(vrsave)); |
369 | } |
370 | |
371 | /** |
372 | * tm_cvmx_set - set CMVX registers |
373 | * @target: The target task. |
374 | * @regset: The user regset structure. |
375 | * @pos: The buffer position. |
376 | * @count: Number of bytes to copy. |
377 | * @kbuf: Kernel buffer to copy into. |
378 | * @ubuf: User buffer to copy from. |
379 | * |
380 | * This function sets in transaction checkpointed VMX registers. |
381 | * |
382 | * When the transaction is active 'ckvr_state' and 'ckvrsave' hold |
383 | * the checkpointed values for the current transaction to fall |
384 | * back on if it aborts in between. The userspace interface buffer |
385 | * layout is as follows. |
386 | * |
387 | * struct data { |
388 | * vector128 vr[32]; |
389 | * vector128 vscr; |
390 | * vector128 vrsave; |
391 | *}; |
392 | */ |
393 | int tm_cvmx_set(struct task_struct *target, const struct user_regset *regset, |
394 | unsigned int pos, unsigned int count, |
395 | const void *kbuf, const void __user *ubuf) |
396 | { |
397 | int ret; |
398 | |
399 | BUILD_BUG_ON(TVSO(vscr) != TVSO(vr[32])); |
400 | |
401 | if (!cpu_has_feature(CPU_FTR_TM)) |
402 | return -ENODEV; |
403 | |
404 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
405 | return -ENODATA; |
406 | |
407 | flush_tmregs_to_thread(target); |
408 | flush_fp_to_thread(target); |
409 | flush_altivec_to_thread(target); |
410 | |
411 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.ckvr_state, |
412 | 0, 33 * sizeof(vector128)); |
413 | if (!ret && count > 0) { |
414 | /* |
415 | * We use only the low-order word of vrsave. |
416 | */ |
417 | union { |
418 | elf_vrreg_t reg; |
419 | u32 word; |
420 | } vrsave; |
421 | memset(&vrsave, 0, sizeof(vrsave)); |
422 | vrsave.word = target->thread.ckvrsave; |
423 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave, |
424 | 33 * sizeof(vector128), -1); |
425 | if (!ret) |
426 | target->thread.ckvrsave = vrsave.word; |
427 | } |
428 | |
429 | return ret; |
430 | } |
431 | |
432 | /** |
433 | * tm_cvsx_active - get active number of registers in CVSX |
434 | * @target: The target task. |
435 | * @regset: The user regset structure. |
436 | * |
437 | * This function checks for the active number of available |
438 | * regisers in transaction checkpointed VSX category. |
439 | */ |
440 | int tm_cvsx_active(struct task_struct *target, const struct user_regset *regset) |
441 | { |
442 | if (!cpu_has_feature(CPU_FTR_TM)) |
443 | return -ENODEV; |
444 | |
445 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
446 | return 0; |
447 | |
448 | flush_vsx_to_thread(target); |
449 | return target->thread.used_vsr ? regset->n : 0; |
450 | } |
451 | |
452 | /** |
453 | * tm_cvsx_get - get CVSX registers |
454 | * @target: The target task. |
455 | * @regset: The user regset structure. |
456 | * @to: Destination of copy. |
457 | * |
458 | * This function gets in transaction checkpointed VSX registers. |
459 | * |
460 | * When the transaction is active 'ckfp_state' holds the checkpointed |
461 | * values for the current transaction to fall back on if it aborts |
462 | * in between. This function gets those checkpointed VSX registers. |
463 | * The userspace interface buffer layout is as follows. |
464 | * |
465 | * struct data { |
466 | * u64 vsx[32]; |
467 | *}; |
468 | */ |
469 | int tm_cvsx_get(struct task_struct *target, const struct user_regset *regset, |
470 | struct membuf to) |
471 | { |
472 | u64 buf[32]; |
473 | int i; |
474 | |
475 | if (!cpu_has_feature(CPU_FTR_TM)) |
476 | return -ENODEV; |
477 | |
478 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
479 | return -ENODATA; |
480 | |
481 | /* Flush the state */ |
482 | flush_tmregs_to_thread(target); |
483 | flush_fp_to_thread(target); |
484 | flush_altivec_to_thread(target); |
485 | flush_vsx_to_thread(target); |
486 | |
487 | for (i = 0; i < 32 ; i++) |
488 | buf[i] = target->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET]; |
489 | return membuf_write(s: &to, v: buf, size: 32 * sizeof(double)); |
490 | } |
491 | |
492 | /** |
493 | * tm_cvsx_set - set CFPR registers |
494 | * @target: The target task. |
495 | * @regset: The user regset structure. |
496 | * @pos: The buffer position. |
497 | * @count: Number of bytes to copy. |
498 | * @kbuf: Kernel buffer to copy into. |
499 | * @ubuf: User buffer to copy from. |
500 | * |
501 | * This function sets in transaction checkpointed VSX registers. |
502 | * |
503 | * When the transaction is active 'ckfp_state' holds the checkpointed |
504 | * VSX register values for the current transaction to fall back on |
505 | * if it aborts in between. This function sets these checkpointed |
506 | * FPR registers. The userspace interface buffer layout is as follows. |
507 | * |
508 | * struct data { |
509 | * u64 vsx[32]; |
510 | *}; |
511 | */ |
512 | int tm_cvsx_set(struct task_struct *target, const struct user_regset *regset, |
513 | unsigned int pos, unsigned int count, |
514 | const void *kbuf, const void __user *ubuf) |
515 | { |
516 | u64 buf[32]; |
517 | int ret, i; |
518 | |
519 | if (!cpu_has_feature(CPU_FTR_TM)) |
520 | return -ENODEV; |
521 | |
522 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
523 | return -ENODATA; |
524 | |
525 | /* Flush the state */ |
526 | flush_tmregs_to_thread(target); |
527 | flush_fp_to_thread(target); |
528 | flush_altivec_to_thread(target); |
529 | flush_vsx_to_thread(target); |
530 | |
531 | for (i = 0; i < 32 ; i++) |
532 | buf[i] = target->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET]; |
533 | |
534 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
535 | data: buf, start_pos: 0, end_pos: 32 * sizeof(double)); |
536 | if (!ret) |
537 | for (i = 0; i < 32 ; i++) |
538 | target->thread.ckfp_state.fpr[i][TS_VSRLOWOFFSET] = buf[i]; |
539 | |
540 | return ret; |
541 | } |
542 | |
543 | /** |
544 | * tm_spr_active - get active number of registers in TM SPR |
545 | * @target: The target task. |
546 | * @regset: The user regset structure. |
547 | * |
548 | * This function checks the active number of available |
549 | * regisers in the transactional memory SPR category. |
550 | */ |
551 | int tm_spr_active(struct task_struct *target, const struct user_regset *regset) |
552 | { |
553 | if (!cpu_has_feature(CPU_FTR_TM)) |
554 | return -ENODEV; |
555 | |
556 | return regset->n; |
557 | } |
558 | |
559 | /** |
560 | * tm_spr_get - get the TM related SPR registers |
561 | * @target: The target task. |
562 | * @regset: The user regset structure. |
563 | * @to: Destination of copy. |
564 | * |
565 | * This function gets transactional memory related SPR registers. |
566 | * The userspace interface buffer layout is as follows. |
567 | * |
568 | * struct { |
569 | * u64 tm_tfhar; |
570 | * u64 tm_texasr; |
571 | * u64 tm_tfiar; |
572 | * }; |
573 | */ |
574 | int tm_spr_get(struct task_struct *target, const struct user_regset *regset, |
575 | struct membuf to) |
576 | { |
577 | /* Build tests */ |
578 | BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr)); |
579 | BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar)); |
580 | BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(ckpt_regs)); |
581 | |
582 | if (!cpu_has_feature(CPU_FTR_TM)) |
583 | return -ENODEV; |
584 | |
585 | /* Flush the states */ |
586 | flush_tmregs_to_thread(target); |
587 | flush_fp_to_thread(target); |
588 | flush_altivec_to_thread(target); |
589 | |
590 | /* TFHAR register */ |
591 | membuf_write(s: &to, v: &target->thread.tm_tfhar, size: sizeof(u64)); |
592 | /* TEXASR register */ |
593 | membuf_write(s: &to, v: &target->thread.tm_texasr, size: sizeof(u64)); |
594 | /* TFIAR register */ |
595 | return membuf_write(s: &to, v: &target->thread.tm_tfiar, size: sizeof(u64)); |
596 | } |
597 | |
598 | /** |
599 | * tm_spr_set - set the TM related SPR registers |
600 | * @target: The target task. |
601 | * @regset: The user regset structure. |
602 | * @pos: The buffer position. |
603 | * @count: Number of bytes to copy. |
604 | * @kbuf: Kernel buffer to copy into. |
605 | * @ubuf: User buffer to copy from. |
606 | * |
607 | * This function sets transactional memory related SPR registers. |
608 | * The userspace interface buffer layout is as follows. |
609 | * |
610 | * struct { |
611 | * u64 tm_tfhar; |
612 | * u64 tm_texasr; |
613 | * u64 tm_tfiar; |
614 | * }; |
615 | */ |
616 | int tm_spr_set(struct task_struct *target, const struct user_regset *regset, |
617 | unsigned int pos, unsigned int count, |
618 | const void *kbuf, const void __user *ubuf) |
619 | { |
620 | int ret; |
621 | |
622 | /* Build tests */ |
623 | BUILD_BUG_ON(TSO(tm_tfhar) + sizeof(u64) != TSO(tm_texasr)); |
624 | BUILD_BUG_ON(TSO(tm_texasr) + sizeof(u64) != TSO(tm_tfiar)); |
625 | BUILD_BUG_ON(TSO(tm_tfiar) + sizeof(u64) != TSO(ckpt_regs)); |
626 | |
627 | if (!cpu_has_feature(CPU_FTR_TM)) |
628 | return -ENODEV; |
629 | |
630 | /* Flush the states */ |
631 | flush_tmregs_to_thread(target); |
632 | flush_fp_to_thread(target); |
633 | flush_altivec_to_thread(target); |
634 | |
635 | /* TFHAR register */ |
636 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
637 | data: &target->thread.tm_tfhar, start_pos: 0, end_pos: sizeof(u64)); |
638 | |
639 | /* TEXASR register */ |
640 | if (!ret) |
641 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
642 | data: &target->thread.tm_texasr, start_pos: sizeof(u64), |
643 | end_pos: 2 * sizeof(u64)); |
644 | |
645 | /* TFIAR register */ |
646 | if (!ret) |
647 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
648 | data: &target->thread.tm_tfiar, |
649 | start_pos: 2 * sizeof(u64), end_pos: 3 * sizeof(u64)); |
650 | return ret; |
651 | } |
652 | |
653 | int tm_tar_active(struct task_struct *target, const struct user_regset *regset) |
654 | { |
655 | if (!cpu_has_feature(CPU_FTR_TM)) |
656 | return -ENODEV; |
657 | |
658 | if (MSR_TM_ACTIVE(target->thread.regs->msr)) |
659 | return regset->n; |
660 | |
661 | return 0; |
662 | } |
663 | |
664 | int tm_tar_get(struct task_struct *target, const struct user_regset *regset, |
665 | struct membuf to) |
666 | { |
667 | if (!cpu_has_feature(CPU_FTR_TM)) |
668 | return -ENODEV; |
669 | |
670 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
671 | return -ENODATA; |
672 | |
673 | return membuf_write(s: &to, v: &target->thread.tm_tar, size: sizeof(u64)); |
674 | } |
675 | |
676 | int tm_tar_set(struct task_struct *target, const struct user_regset *regset, |
677 | unsigned int pos, unsigned int count, |
678 | const void *kbuf, const void __user *ubuf) |
679 | { |
680 | int ret; |
681 | |
682 | if (!cpu_has_feature(CPU_FTR_TM)) |
683 | return -ENODEV; |
684 | |
685 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
686 | return -ENODATA; |
687 | |
688 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
689 | data: &target->thread.tm_tar, start_pos: 0, end_pos: sizeof(u64)); |
690 | return ret; |
691 | } |
692 | |
693 | int tm_ppr_active(struct task_struct *target, const struct user_regset *regset) |
694 | { |
695 | if (!cpu_has_feature(CPU_FTR_TM)) |
696 | return -ENODEV; |
697 | |
698 | if (MSR_TM_ACTIVE(target->thread.regs->msr)) |
699 | return regset->n; |
700 | |
701 | return 0; |
702 | } |
703 | |
704 | |
705 | int tm_ppr_get(struct task_struct *target, const struct user_regset *regset, |
706 | struct membuf to) |
707 | { |
708 | if (!cpu_has_feature(CPU_FTR_TM)) |
709 | return -ENODEV; |
710 | |
711 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
712 | return -ENODATA; |
713 | |
714 | return membuf_write(s: &to, v: &target->thread.tm_ppr, size: sizeof(u64)); |
715 | } |
716 | |
717 | int tm_ppr_set(struct task_struct *target, const struct user_regset *regset, |
718 | unsigned int pos, unsigned int count, |
719 | const void *kbuf, const void __user *ubuf) |
720 | { |
721 | int ret; |
722 | |
723 | if (!cpu_has_feature(CPU_FTR_TM)) |
724 | return -ENODEV; |
725 | |
726 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
727 | return -ENODATA; |
728 | |
729 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
730 | data: &target->thread.tm_ppr, start_pos: 0, end_pos: sizeof(u64)); |
731 | return ret; |
732 | } |
733 | |
734 | int tm_dscr_active(struct task_struct *target, const struct user_regset *regset) |
735 | { |
736 | if (!cpu_has_feature(CPU_FTR_TM)) |
737 | return -ENODEV; |
738 | |
739 | if (MSR_TM_ACTIVE(target->thread.regs->msr)) |
740 | return regset->n; |
741 | |
742 | return 0; |
743 | } |
744 | |
745 | int tm_dscr_get(struct task_struct *target, const struct user_regset *regset, |
746 | struct membuf to) |
747 | { |
748 | if (!cpu_has_feature(CPU_FTR_TM)) |
749 | return -ENODEV; |
750 | |
751 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
752 | return -ENODATA; |
753 | |
754 | return membuf_write(s: &to, v: &target->thread.tm_dscr, size: sizeof(u64)); |
755 | } |
756 | |
757 | int tm_dscr_set(struct task_struct *target, const struct user_regset *regset, |
758 | unsigned int pos, unsigned int count, |
759 | const void *kbuf, const void __user *ubuf) |
760 | { |
761 | int ret; |
762 | |
763 | if (!cpu_has_feature(CPU_FTR_TM)) |
764 | return -ENODEV; |
765 | |
766 | if (!MSR_TM_ACTIVE(target->thread.regs->msr)) |
767 | return -ENODATA; |
768 | |
769 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
770 | data: &target->thread.tm_dscr, start_pos: 0, end_pos: sizeof(u64)); |
771 | return ret; |
772 | } |
773 | |
774 | int tm_cgpr32_get(struct task_struct *target, const struct user_regset *regset, |
775 | struct membuf to) |
776 | { |
777 | gpr32_get_common(target, regset, to, |
778 | regs: &target->thread.ckpt_regs.gpr[0]); |
779 | return membuf_zero(&to, ELF_NGREG * sizeof(u32)); |
780 | } |
781 | |
782 | int tm_cgpr32_set(struct task_struct *target, const struct user_regset *regset, |
783 | unsigned int pos, unsigned int count, |
784 | const void *kbuf, const void __user *ubuf) |
785 | { |
786 | return gpr32_set_common(target, regset, pos, count, kbuf, ubuf, |
787 | regs: &target->thread.ckpt_regs.gpr[0]); |
788 | } |
789 | |