1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | #include <linux/regset.h> |
4 | #include <linux/elf.h> |
5 | #include <linux/nospec.h> |
6 | #include <linux/pkeys.h> |
7 | |
8 | #include "ptrace-decl.h" |
9 | |
10 | struct pt_regs_offset { |
11 | const char *name; |
12 | int offset; |
13 | }; |
14 | |
15 | #define STR(s) #s /* convert to string */ |
16 | #define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)} |
17 | #define GPR_OFFSET_NAME(num) \ |
18 | {.name = STR(r##num), .offset = offsetof(struct pt_regs, gpr[num])}, \ |
19 | {.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])} |
20 | #define REG_OFFSET_END {.name = NULL, .offset = 0} |
21 | |
22 | static const struct pt_regs_offset regoffset_table[] = { |
23 | GPR_OFFSET_NAME(0), |
24 | GPR_OFFSET_NAME(1), |
25 | GPR_OFFSET_NAME(2), |
26 | GPR_OFFSET_NAME(3), |
27 | GPR_OFFSET_NAME(4), |
28 | GPR_OFFSET_NAME(5), |
29 | GPR_OFFSET_NAME(6), |
30 | GPR_OFFSET_NAME(7), |
31 | GPR_OFFSET_NAME(8), |
32 | GPR_OFFSET_NAME(9), |
33 | GPR_OFFSET_NAME(10), |
34 | GPR_OFFSET_NAME(11), |
35 | GPR_OFFSET_NAME(12), |
36 | GPR_OFFSET_NAME(13), |
37 | GPR_OFFSET_NAME(14), |
38 | GPR_OFFSET_NAME(15), |
39 | GPR_OFFSET_NAME(16), |
40 | GPR_OFFSET_NAME(17), |
41 | GPR_OFFSET_NAME(18), |
42 | GPR_OFFSET_NAME(19), |
43 | GPR_OFFSET_NAME(20), |
44 | GPR_OFFSET_NAME(21), |
45 | GPR_OFFSET_NAME(22), |
46 | GPR_OFFSET_NAME(23), |
47 | GPR_OFFSET_NAME(24), |
48 | GPR_OFFSET_NAME(25), |
49 | GPR_OFFSET_NAME(26), |
50 | GPR_OFFSET_NAME(27), |
51 | GPR_OFFSET_NAME(28), |
52 | GPR_OFFSET_NAME(29), |
53 | GPR_OFFSET_NAME(30), |
54 | GPR_OFFSET_NAME(31), |
55 | REG_OFFSET_NAME(nip), |
56 | REG_OFFSET_NAME(msr), |
57 | REG_OFFSET_NAME(ctr), |
58 | REG_OFFSET_NAME(link), |
59 | REG_OFFSET_NAME(xer), |
60 | REG_OFFSET_NAME(ccr), |
61 | #ifdef CONFIG_PPC64 |
62 | REG_OFFSET_NAME(softe), |
63 | #else |
64 | REG_OFFSET_NAME(mq), |
65 | #endif |
66 | REG_OFFSET_NAME(trap), |
67 | REG_OFFSET_NAME(dar), |
68 | REG_OFFSET_NAME(dsisr), |
69 | REG_OFFSET_END, |
70 | }; |
71 | |
72 | /** |
73 | * regs_query_register_offset() - query register offset from its name |
74 | * @name: the name of a register |
75 | * |
76 | * regs_query_register_offset() returns the offset of a register in struct |
77 | * pt_regs from its name. If the name is invalid, this returns -EINVAL; |
78 | */ |
79 | int regs_query_register_offset(const char *name) |
80 | { |
81 | const struct pt_regs_offset *roff; |
82 | for (roff = regoffset_table; roff->name != NULL; roff++) |
83 | if (!strcmp(roff->name, name)) |
84 | return roff->offset; |
85 | return -EINVAL; |
86 | } |
87 | |
88 | /** |
89 | * regs_query_register_name() - query register name from its offset |
90 | * @offset: the offset of a register in struct pt_regs. |
91 | * |
92 | * regs_query_register_name() returns the name of a register from its |
93 | * offset in struct pt_regs. If the @offset is invalid, this returns NULL; |
94 | */ |
95 | const char *regs_query_register_name(unsigned int offset) |
96 | { |
97 | const struct pt_regs_offset *roff; |
98 | for (roff = regoffset_table; roff->name != NULL; roff++) |
99 | if (roff->offset == offset) |
100 | return roff->name; |
101 | return NULL; |
102 | } |
103 | |
104 | /* |
105 | * does not yet catch signals sent when the child dies. |
106 | * in exit.c or in signal.c. |
107 | */ |
108 | |
109 | static unsigned long get_user_msr(struct task_struct *task) |
110 | { |
111 | return task->thread.regs->msr | task->thread.fpexc_mode; |
112 | } |
113 | |
114 | static __always_inline int set_user_msr(struct task_struct *task, unsigned long msr) |
115 | { |
116 | unsigned long newmsr = (task->thread.regs->msr & ~MSR_DEBUGCHANGE) | |
117 | (msr & MSR_DEBUGCHANGE); |
118 | regs_set_return_msr(task->thread.regs, newmsr); |
119 | return 0; |
120 | } |
121 | |
122 | #ifdef CONFIG_PPC64 |
123 | static int get_user_dscr(struct task_struct *task, unsigned long *data) |
124 | { |
125 | *data = task->thread.dscr; |
126 | return 0; |
127 | } |
128 | |
129 | static int set_user_dscr(struct task_struct *task, unsigned long dscr) |
130 | { |
131 | task->thread.dscr = dscr; |
132 | task->thread.dscr_inherit = 1; |
133 | return 0; |
134 | } |
135 | #else |
136 | static int get_user_dscr(struct task_struct *task, unsigned long *data) |
137 | { |
138 | return -EIO; |
139 | } |
140 | |
141 | static int set_user_dscr(struct task_struct *task, unsigned long dscr) |
142 | { |
143 | return -EIO; |
144 | } |
145 | #endif |
146 | |
147 | /* |
148 | * We prevent mucking around with the reserved area of trap |
149 | * which are used internally by the kernel. |
150 | */ |
151 | static __always_inline int set_user_trap(struct task_struct *task, unsigned long trap) |
152 | { |
153 | set_trap(task->thread.regs, trap); |
154 | return 0; |
155 | } |
156 | |
157 | /* |
158 | * Get contents of register REGNO in task TASK. |
159 | */ |
160 | int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data) |
161 | { |
162 | unsigned int regs_max; |
163 | |
164 | if (task->thread.regs == NULL || !data) |
165 | return -EIO; |
166 | |
167 | if (regno == PT_MSR) { |
168 | *data = get_user_msr(task); |
169 | return 0; |
170 | } |
171 | |
172 | if (regno == PT_DSCR) |
173 | return get_user_dscr(task, data); |
174 | |
175 | /* |
176 | * softe copies paca->irq_soft_mask variable state. Since irq_soft_mask is |
177 | * no more used as a flag, lets force usr to always see the softe value as 1 |
178 | * which means interrupts are not soft disabled. |
179 | */ |
180 | if (IS_ENABLED(CONFIG_PPC64) && regno == PT_SOFTE) { |
181 | *data = 1; |
182 | return 0; |
183 | } |
184 | |
185 | regs_max = sizeof(struct user_pt_regs) / sizeof(unsigned long); |
186 | if (regno < regs_max) { |
187 | regno = array_index_nospec(regno, regs_max); |
188 | *data = ((unsigned long *)task->thread.regs)[regno]; |
189 | return 0; |
190 | } |
191 | |
192 | return -EIO; |
193 | } |
194 | |
195 | /* |
196 | * Write contents of register REGNO in task TASK. |
197 | */ |
198 | int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) |
199 | { |
200 | if (task->thread.regs == NULL) |
201 | return -EIO; |
202 | |
203 | if (regno == PT_MSR) |
204 | return set_user_msr(task, msr: data); |
205 | if (regno == PT_TRAP) |
206 | return set_user_trap(task, trap: data); |
207 | if (regno == PT_DSCR) |
208 | return set_user_dscr(task, dscr: data); |
209 | |
210 | if (regno <= PT_MAX_PUT_REG) { |
211 | regno = array_index_nospec(regno, PT_MAX_PUT_REG + 1); |
212 | ((unsigned long *)task->thread.regs)[regno] = data; |
213 | return 0; |
214 | } |
215 | return -EIO; |
216 | } |
217 | |
218 | static int gpr_get(struct task_struct *target, const struct user_regset *regset, |
219 | struct membuf to) |
220 | { |
221 | struct membuf to_msr = membuf_at(&to, offsetof(struct pt_regs, msr)); |
222 | #ifdef CONFIG_PPC64 |
223 | struct membuf to_softe = membuf_at(&to, offsetof(struct pt_regs, softe)); |
224 | #endif |
225 | if (target->thread.regs == NULL) |
226 | return -EIO; |
227 | |
228 | membuf_write(&to, target->thread.regs, sizeof(struct user_pt_regs)); |
229 | |
230 | membuf_store(&to_msr, get_user_msr(target)); |
231 | #ifdef CONFIG_PPC64 |
232 | membuf_store(&to_softe, 0x1ul); |
233 | #endif |
234 | return membuf_zero(&to, ELF_NGREG * sizeof(unsigned long) - |
235 | sizeof(struct user_pt_regs)); |
236 | } |
237 | |
238 | static int gpr_set(struct task_struct *target, const struct user_regset *regset, |
239 | unsigned int pos, unsigned int count, const void *kbuf, |
240 | const void __user *ubuf) |
241 | { |
242 | unsigned long reg; |
243 | int ret; |
244 | |
245 | if (target->thread.regs == NULL) |
246 | return -EIO; |
247 | |
248 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
249 | data: target->thread.regs, |
250 | start_pos: 0, end_pos: PT_MSR * sizeof(reg)); |
251 | |
252 | if (!ret && count > 0) { |
253 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, |
254 | PT_MSR * sizeof(reg), |
255 | (PT_MSR + 1) * sizeof(reg)); |
256 | if (!ret) |
257 | ret = set_user_msr(task: target, msr: reg); |
258 | } |
259 | |
260 | BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != |
261 | offsetof(struct pt_regs, msr) + sizeof(long)); |
262 | |
263 | if (!ret) |
264 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
265 | &target->thread.regs->orig_gpr3, |
266 | PT_ORIG_R3 * sizeof(reg), |
267 | (PT_MAX_PUT_REG + 1) * sizeof(reg)); |
268 | |
269 | if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) |
270 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
271 | (PT_MAX_PUT_REG + 1) * sizeof(reg), |
272 | PT_TRAP * sizeof(reg)); |
273 | |
274 | if (!ret && count > 0) { |
275 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, data: ®, |
276 | start_pos: PT_TRAP * sizeof(reg), |
277 | end_pos: (PT_TRAP + 1) * sizeof(reg)); |
278 | if (!ret) |
279 | ret = set_user_trap(task: target, trap: reg); |
280 | } |
281 | |
282 | if (!ret) |
283 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
284 | start_pos: (PT_TRAP + 1) * sizeof(reg), end_pos: -1); |
285 | |
286 | return ret; |
287 | } |
288 | |
289 | #ifdef CONFIG_PPC64 |
290 | static int ppr_get(struct task_struct *target, const struct user_regset *regset, |
291 | struct membuf to) |
292 | { |
293 | if (!target->thread.regs) |
294 | return -EINVAL; |
295 | |
296 | return membuf_write(&to, &target->thread.regs->ppr, sizeof(u64)); |
297 | } |
298 | |
299 | static int ppr_set(struct task_struct *target, const struct user_regset *regset, |
300 | unsigned int pos, unsigned int count, const void *kbuf, |
301 | const void __user *ubuf) |
302 | { |
303 | if (!target->thread.regs) |
304 | return -EINVAL; |
305 | |
306 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
307 | &target->thread.regs->ppr, 0, sizeof(u64)); |
308 | } |
309 | |
310 | static int dscr_get(struct task_struct *target, const struct user_regset *regset, |
311 | struct membuf to) |
312 | { |
313 | return membuf_write(&to, &target->thread.dscr, sizeof(u64)); |
314 | } |
315 | static int dscr_set(struct task_struct *target, const struct user_regset *regset, |
316 | unsigned int pos, unsigned int count, const void *kbuf, |
317 | const void __user *ubuf) |
318 | { |
319 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
320 | &target->thread.dscr, 0, sizeof(u64)); |
321 | } |
322 | #endif |
323 | #ifdef CONFIG_PPC_BOOK3S_64 |
324 | static int tar_get(struct task_struct *target, const struct user_regset *regset, |
325 | struct membuf to) |
326 | { |
327 | return membuf_write(&to, &target->thread.tar, sizeof(u64)); |
328 | } |
329 | static int tar_set(struct task_struct *target, const struct user_regset *regset, |
330 | unsigned int pos, unsigned int count, const void *kbuf, |
331 | const void __user *ubuf) |
332 | { |
333 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
334 | &target->thread.tar, 0, sizeof(u64)); |
335 | } |
336 | |
337 | static int ebb_active(struct task_struct *target, const struct user_regset *regset) |
338 | { |
339 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
340 | return -ENODEV; |
341 | |
342 | if (target->thread.used_ebb) |
343 | return regset->n; |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | static int ebb_get(struct task_struct *target, const struct user_regset *regset, |
349 | struct membuf to) |
350 | { |
351 | /* Build tests */ |
352 | BUILD_BUG_ON(TSO(ebbrr) + sizeof(unsigned long) != TSO(ebbhr)); |
353 | BUILD_BUG_ON(TSO(ebbhr) + sizeof(unsigned long) != TSO(bescr)); |
354 | |
355 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
356 | return -ENODEV; |
357 | |
358 | if (!target->thread.used_ebb) |
359 | return -ENODATA; |
360 | |
361 | return membuf_write(&to, &target->thread.ebbrr, 3 * sizeof(unsigned long)); |
362 | } |
363 | |
364 | static int ebb_set(struct task_struct *target, const struct user_regset *regset, |
365 | unsigned int pos, unsigned int count, const void *kbuf, |
366 | const void __user *ubuf) |
367 | { |
368 | int ret = 0; |
369 | |
370 | /* Build tests */ |
371 | BUILD_BUG_ON(TSO(ebbrr) + sizeof(unsigned long) != TSO(ebbhr)); |
372 | BUILD_BUG_ON(TSO(ebbhr) + sizeof(unsigned long) != TSO(bescr)); |
373 | |
374 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
375 | return -ENODEV; |
376 | |
377 | if (target->thread.used_ebb) |
378 | return -ENODATA; |
379 | |
380 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.ebbrr, |
381 | 0, sizeof(unsigned long)); |
382 | |
383 | if (!ret) |
384 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
385 | &target->thread.ebbhr, sizeof(unsigned long), |
386 | 2 * sizeof(unsigned long)); |
387 | |
388 | if (!ret) |
389 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
390 | &target->thread.bescr, 2 * sizeof(unsigned long), |
391 | 3 * sizeof(unsigned long)); |
392 | |
393 | return ret; |
394 | } |
395 | static int pmu_active(struct task_struct *target, const struct user_regset *regset) |
396 | { |
397 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
398 | return -ENODEV; |
399 | |
400 | return regset->n; |
401 | } |
402 | |
403 | static int pmu_get(struct task_struct *target, const struct user_regset *regset, |
404 | struct membuf to) |
405 | { |
406 | /* Build tests */ |
407 | BUILD_BUG_ON(TSO(siar) + sizeof(unsigned long) != TSO(sdar)); |
408 | BUILD_BUG_ON(TSO(sdar) + sizeof(unsigned long) != TSO(sier)); |
409 | BUILD_BUG_ON(TSO(sier) + sizeof(unsigned long) != TSO(mmcr2)); |
410 | BUILD_BUG_ON(TSO(mmcr2) + sizeof(unsigned long) != TSO(mmcr0)); |
411 | |
412 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
413 | return -ENODEV; |
414 | |
415 | return membuf_write(&to, &target->thread.siar, 5 * sizeof(unsigned long)); |
416 | } |
417 | |
418 | static int pmu_set(struct task_struct *target, const struct user_regset *regset, |
419 | unsigned int pos, unsigned int count, const void *kbuf, |
420 | const void __user *ubuf) |
421 | { |
422 | int ret = 0; |
423 | |
424 | /* Build tests */ |
425 | BUILD_BUG_ON(TSO(siar) + sizeof(unsigned long) != TSO(sdar)); |
426 | BUILD_BUG_ON(TSO(sdar) + sizeof(unsigned long) != TSO(sier)); |
427 | BUILD_BUG_ON(TSO(sier) + sizeof(unsigned long) != TSO(mmcr2)); |
428 | BUILD_BUG_ON(TSO(mmcr2) + sizeof(unsigned long) != TSO(mmcr0)); |
429 | |
430 | if (!cpu_has_feature(CPU_FTR_ARCH_207S)) |
431 | return -ENODEV; |
432 | |
433 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.siar, |
434 | 0, sizeof(unsigned long)); |
435 | |
436 | if (!ret) |
437 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
438 | &target->thread.sdar, sizeof(unsigned long), |
439 | 2 * sizeof(unsigned long)); |
440 | |
441 | if (!ret) |
442 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
443 | &target->thread.sier, 2 * sizeof(unsigned long), |
444 | 3 * sizeof(unsigned long)); |
445 | |
446 | if (!ret) |
447 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
448 | &target->thread.mmcr2, 3 * sizeof(unsigned long), |
449 | 4 * sizeof(unsigned long)); |
450 | |
451 | if (!ret) |
452 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
453 | &target->thread.mmcr0, 4 * sizeof(unsigned long), |
454 | 5 * sizeof(unsigned long)); |
455 | return ret; |
456 | } |
457 | |
458 | static int dexcr_active(struct task_struct *target, const struct user_regset *regset) |
459 | { |
460 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
461 | return -ENODEV; |
462 | |
463 | return regset->n; |
464 | } |
465 | |
466 | static int dexcr_get(struct task_struct *target, const struct user_regset *regset, |
467 | struct membuf to) |
468 | { |
469 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
470 | return -ENODEV; |
471 | |
472 | /* |
473 | * The DEXCR is currently static across all CPUs, so we don't |
474 | * store the target's value anywhere, but the static value |
475 | * will also be correct. |
476 | */ |
477 | membuf_store(&to, (u64)lower_32_bits(DEXCR_INIT)); |
478 | |
479 | /* |
480 | * Technically the HDEXCR is per-cpu, but a hypervisor can't reasonably |
481 | * change it between CPUs of the same guest. |
482 | */ |
483 | return membuf_store(&to, (u64)lower_32_bits(mfspr(SPRN_HDEXCR_RO))); |
484 | } |
485 | |
486 | #ifdef CONFIG_CHECKPOINT_RESTORE |
487 | static int hashkeyr_active(struct task_struct *target, const struct user_regset *regset) |
488 | { |
489 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
490 | return -ENODEV; |
491 | |
492 | return regset->n; |
493 | } |
494 | |
495 | static int hashkeyr_get(struct task_struct *target, const struct user_regset *regset, |
496 | struct membuf to) |
497 | { |
498 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
499 | return -ENODEV; |
500 | |
501 | return membuf_store(&to, target->thread.hashkeyr); |
502 | } |
503 | |
504 | static int hashkeyr_set(struct task_struct *target, const struct user_regset *regset, |
505 | unsigned int pos, unsigned int count, const void *kbuf, |
506 | const void __user *ubuf) |
507 | { |
508 | if (!cpu_has_feature(CPU_FTR_ARCH_31)) |
509 | return -ENODEV; |
510 | |
511 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.hashkeyr, |
512 | 0, sizeof(unsigned long)); |
513 | } |
514 | #endif /* CONFIG_CHECKPOINT_RESTORE */ |
515 | #endif /* CONFIG_PPC_BOOK3S_64 */ |
516 | |
517 | #ifdef CONFIG_PPC_MEM_KEYS |
518 | static int pkey_active(struct task_struct *target, const struct user_regset *regset) |
519 | { |
520 | if (!arch_pkeys_enabled()) |
521 | return -ENODEV; |
522 | |
523 | return regset->n; |
524 | } |
525 | |
526 | static int pkey_get(struct task_struct *target, const struct user_regset *regset, |
527 | struct membuf to) |
528 | { |
529 | |
530 | if (!arch_pkeys_enabled()) |
531 | return -ENODEV; |
532 | |
533 | membuf_store(&to, target->thread.regs->amr); |
534 | membuf_store(&to, target->thread.regs->iamr); |
535 | return membuf_store(&to, default_uamor); |
536 | } |
537 | |
538 | static int pkey_set(struct task_struct *target, const struct user_regset *regset, |
539 | unsigned int pos, unsigned int count, const void *kbuf, |
540 | const void __user *ubuf) |
541 | { |
542 | u64 new_amr; |
543 | int ret; |
544 | |
545 | if (!arch_pkeys_enabled()) |
546 | return -ENODEV; |
547 | |
548 | /* Only the AMR can be set from userspace */ |
549 | if (pos != 0 || count != sizeof(new_amr)) |
550 | return -EINVAL; |
551 | |
552 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
553 | &new_amr, 0, sizeof(new_amr)); |
554 | if (ret) |
555 | return ret; |
556 | |
557 | /* |
558 | * UAMOR determines which bits of the AMR can be set from userspace. |
559 | * UAMOR value 0b11 indicates that the AMR value can be modified |
560 | * from userspace. If the kernel is using a specific key, we avoid |
561 | * userspace modifying the AMR value for that key by masking them |
562 | * via UAMOR 0b00. |
563 | * |
564 | * Pick the AMR values for the keys that kernel is using. This |
565 | * will be indicated by the ~default_uamor bits. |
566 | */ |
567 | target->thread.regs->amr = (new_amr & default_uamor) | |
568 | (target->thread.regs->amr & ~default_uamor); |
569 | |
570 | return 0; |
571 | } |
572 | #endif /* CONFIG_PPC_MEM_KEYS */ |
573 | |
574 | static const struct user_regset native_regsets[] = { |
575 | [REGSET_GPR] = { |
576 | .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, |
577 | .size = sizeof(long), .align = sizeof(long), |
578 | .regset_get = gpr_get, .set = gpr_set |
579 | }, |
580 | [REGSET_FPR] = { |
581 | .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, |
582 | .size = sizeof(double), .align = sizeof(double), |
583 | .regset_get = fpr_get, .set = fpr_set |
584 | }, |
585 | #ifdef CONFIG_ALTIVEC |
586 | [REGSET_VMX] = { |
587 | .core_note_type = NT_PPC_VMX, .n = 34, |
588 | .size = sizeof(vector128), .align = sizeof(vector128), |
589 | .active = vr_active, .regset_get = vr_get, .set = vr_set |
590 | }, |
591 | #endif |
592 | #ifdef CONFIG_VSX |
593 | [REGSET_VSX] = { |
594 | .core_note_type = NT_PPC_VSX, .n = 32, |
595 | .size = sizeof(double), .align = sizeof(double), |
596 | .active = vsr_active, .regset_get = vsr_get, .set = vsr_set |
597 | }, |
598 | #endif |
599 | #ifdef CONFIG_SPE |
600 | [REGSET_SPE] = { |
601 | .core_note_type = NT_PPC_SPE, .n = 35, |
602 | .size = sizeof(u32), .align = sizeof(u32), |
603 | .active = evr_active, .regset_get = evr_get, .set = evr_set |
604 | }, |
605 | #endif |
606 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
607 | [REGSET_TM_CGPR] = { |
608 | .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, |
609 | .size = sizeof(long), .align = sizeof(long), |
610 | .active = tm_cgpr_active, .regset_get = tm_cgpr_get, .set = tm_cgpr_set |
611 | }, |
612 | [REGSET_TM_CFPR] = { |
613 | .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, |
614 | .size = sizeof(double), .align = sizeof(double), |
615 | .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set |
616 | }, |
617 | [REGSET_TM_CVMX] = { |
618 | .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, |
619 | .size = sizeof(vector128), .align = sizeof(vector128), |
620 | .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set |
621 | }, |
622 | [REGSET_TM_CVSX] = { |
623 | .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, |
624 | .size = sizeof(double), .align = sizeof(double), |
625 | .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set |
626 | }, |
627 | [REGSET_TM_SPR] = { |
628 | .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, |
629 | .size = sizeof(u64), .align = sizeof(u64), |
630 | .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set |
631 | }, |
632 | [REGSET_TM_CTAR] = { |
633 | .core_note_type = NT_PPC_TM_CTAR, .n = 1, |
634 | .size = sizeof(u64), .align = sizeof(u64), |
635 | .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set |
636 | }, |
637 | [REGSET_TM_CPPR] = { |
638 | .core_note_type = NT_PPC_TM_CPPR, .n = 1, |
639 | .size = sizeof(u64), .align = sizeof(u64), |
640 | .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set |
641 | }, |
642 | [REGSET_TM_CDSCR] = { |
643 | .core_note_type = NT_PPC_TM_CDSCR, .n = 1, |
644 | .size = sizeof(u64), .align = sizeof(u64), |
645 | .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set |
646 | }, |
647 | #endif |
648 | #ifdef CONFIG_PPC64 |
649 | [REGSET_PPR] = { |
650 | .core_note_type = NT_PPC_PPR, .n = 1, |
651 | .size = sizeof(u64), .align = sizeof(u64), |
652 | .regset_get = ppr_get, .set = ppr_set |
653 | }, |
654 | [REGSET_DSCR] = { |
655 | .core_note_type = NT_PPC_DSCR, .n = 1, |
656 | .size = sizeof(u64), .align = sizeof(u64), |
657 | .regset_get = dscr_get, .set = dscr_set |
658 | }, |
659 | #endif |
660 | #ifdef CONFIG_PPC_BOOK3S_64 |
661 | [REGSET_TAR] = { |
662 | .core_note_type = NT_PPC_TAR, .n = 1, |
663 | .size = sizeof(u64), .align = sizeof(u64), |
664 | .regset_get = tar_get, .set = tar_set |
665 | }, |
666 | [REGSET_EBB] = { |
667 | .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, |
668 | .size = sizeof(u64), .align = sizeof(u64), |
669 | .active = ebb_active, .regset_get = ebb_get, .set = ebb_set |
670 | }, |
671 | [REGSET_PMR] = { |
672 | .core_note_type = NT_PPC_PMU, .n = ELF_NPMU, |
673 | .size = sizeof(u64), .align = sizeof(u64), |
674 | .active = pmu_active, .regset_get = pmu_get, .set = pmu_set |
675 | }, |
676 | [REGSET_DEXCR] = { |
677 | .core_note_type = NT_PPC_DEXCR, .n = ELF_NDEXCR, |
678 | .size = sizeof(u64), .align = sizeof(u64), |
679 | .active = dexcr_active, .regset_get = dexcr_get |
680 | }, |
681 | #ifdef CONFIG_CHECKPOINT_RESTORE |
682 | [REGSET_HASHKEYR] = { |
683 | .core_note_type = NT_PPC_HASHKEYR, .n = ELF_NHASHKEYR, |
684 | .size = sizeof(u64), .align = sizeof(u64), |
685 | .active = hashkeyr_active, .regset_get = hashkeyr_get, .set = hashkeyr_set |
686 | }, |
687 | #endif |
688 | #endif |
689 | #ifdef CONFIG_PPC_MEM_KEYS |
690 | [REGSET_PKEY] = { |
691 | .core_note_type = NT_PPC_PKEY, .n = ELF_NPKEY, |
692 | .size = sizeof(u64), .align = sizeof(u64), |
693 | .active = pkey_active, .regset_get = pkey_get, .set = pkey_set |
694 | }, |
695 | #endif |
696 | }; |
697 | |
698 | const struct user_regset_view user_ppc_native_view = { |
699 | .name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, |
700 | .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) |
701 | }; |
702 | |
703 | #include <linux/compat.h> |
704 | |
705 | int gpr32_get_common(struct task_struct *target, |
706 | const struct user_regset *regset, |
707 | struct membuf to, unsigned long *regs) |
708 | { |
709 | int i; |
710 | |
711 | for (i = 0; i < PT_MSR; i++) |
712 | membuf_store(&to, (u32)regs[i]); |
713 | membuf_store(&to, (u32)get_user_msr(target)); |
714 | for (i++ ; i < PT_REGS_COUNT; i++) |
715 | membuf_store(&to, (u32)regs[i]); |
716 | return membuf_zero(&to, (ELF_NGREG - PT_REGS_COUNT) * sizeof(u32)); |
717 | } |
718 | |
719 | static int gpr32_set_common_kernel(struct task_struct *target, |
720 | const struct user_regset *regset, |
721 | unsigned int pos, unsigned int count, |
722 | const void *kbuf, unsigned long *regs) |
723 | { |
724 | const compat_ulong_t *k = kbuf; |
725 | |
726 | pos /= sizeof(compat_ulong_t); |
727 | count /= sizeof(compat_ulong_t); |
728 | |
729 | for (; count > 0 && pos < PT_MSR; --count) |
730 | regs[pos++] = *k++; |
731 | |
732 | if (count > 0 && pos == PT_MSR) { |
733 | set_user_msr(task: target, msr: *k++); |
734 | ++pos; |
735 | --count; |
736 | } |
737 | |
738 | for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) |
739 | regs[pos++] = *k++; |
740 | for (; count > 0 && pos < PT_TRAP; --count, ++pos) |
741 | ++k; |
742 | |
743 | if (count > 0 && pos == PT_TRAP) { |
744 | set_user_trap(task: target, trap: *k++); |
745 | ++pos; |
746 | --count; |
747 | } |
748 | |
749 | kbuf = k; |
750 | pos *= sizeof(compat_ulong_t); |
751 | count *= sizeof(compat_ulong_t); |
752 | user_regset_copyin_ignore(&pos, &count, &kbuf, NULL, |
753 | (PT_TRAP + 1) * sizeof(compat_ulong_t), -1); |
754 | return 0; |
755 | } |
756 | |
757 | static int gpr32_set_common_user(struct task_struct *target, |
758 | const struct user_regset *regset, |
759 | unsigned int pos, unsigned int count, |
760 | const void __user *ubuf, unsigned long *regs) |
761 | { |
762 | const compat_ulong_t __user *u = ubuf; |
763 | const void *kbuf = NULL; |
764 | compat_ulong_t reg; |
765 | |
766 | if (!user_read_access_begin(u, count)) |
767 | return -EFAULT; |
768 | |
769 | pos /= sizeof(reg); |
770 | count /= sizeof(reg); |
771 | |
772 | for (; count > 0 && pos < PT_MSR; --count) { |
773 | unsafe_get_user(reg, u++, Efault); |
774 | regs[pos++] = reg; |
775 | } |
776 | |
777 | if (count > 0 && pos == PT_MSR) { |
778 | unsafe_get_user(reg, u++, Efault); |
779 | set_user_msr(task: target, msr: reg); |
780 | ++pos; |
781 | --count; |
782 | } |
783 | |
784 | for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) { |
785 | unsafe_get_user(reg, u++, Efault); |
786 | regs[pos++] = reg; |
787 | } |
788 | for (; count > 0 && pos < PT_TRAP; --count, ++pos) |
789 | unsafe_get_user(reg, u++, Efault); |
790 | |
791 | if (count > 0 && pos == PT_TRAP) { |
792 | unsafe_get_user(reg, u++, Efault); |
793 | set_user_trap(task: target, trap: reg); |
794 | ++pos; |
795 | --count; |
796 | } |
797 | user_read_access_end(); |
798 | |
799 | ubuf = u; |
800 | pos *= sizeof(reg); |
801 | count *= sizeof(reg); |
802 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
803 | (PT_TRAP + 1) * sizeof(reg), -1); |
804 | return 0; |
805 | |
806 | Efault: |
807 | user_read_access_end(); |
808 | return -EFAULT; |
809 | } |
810 | |
811 | int gpr32_set_common(struct task_struct *target, |
812 | const struct user_regset *regset, |
813 | unsigned int pos, unsigned int count, |
814 | const void *kbuf, const void __user *ubuf, |
815 | unsigned long *regs) |
816 | { |
817 | if (kbuf) |
818 | return gpr32_set_common_kernel(target, regset, pos, count, kbuf, regs); |
819 | else |
820 | return gpr32_set_common_user(target, regset, pos, count, ubuf, regs); |
821 | } |
822 | |
823 | static int gpr32_get(struct task_struct *target, |
824 | const struct user_regset *regset, |
825 | struct membuf to) |
826 | { |
827 | if (target->thread.regs == NULL) |
828 | return -EIO; |
829 | |
830 | return gpr32_get_common(target, regset, to, |
831 | regs: &target->thread.regs->gpr[0]); |
832 | } |
833 | |
834 | static int gpr32_set(struct task_struct *target, |
835 | const struct user_regset *regset, |
836 | unsigned int pos, unsigned int count, |
837 | const void *kbuf, const void __user *ubuf) |
838 | { |
839 | if (target->thread.regs == NULL) |
840 | return -EIO; |
841 | |
842 | return gpr32_set_common(target, regset, pos, count, kbuf, ubuf, |
843 | regs: &target->thread.regs->gpr[0]); |
844 | } |
845 | |
846 | /* |
847 | * These are the regset flavors matching the CONFIG_PPC32 native set. |
848 | */ |
849 | static const struct user_regset compat_regsets[] = { |
850 | [REGSET_GPR] = { |
851 | .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, |
852 | .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), |
853 | .regset_get = gpr32_get, .set = gpr32_set |
854 | }, |
855 | [REGSET_FPR] = { |
856 | .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, |
857 | .size = sizeof(double), .align = sizeof(double), |
858 | .regset_get = fpr_get, .set = fpr_set |
859 | }, |
860 | #ifdef CONFIG_ALTIVEC |
861 | [REGSET_VMX] = { |
862 | .core_note_type = NT_PPC_VMX, .n = 34, |
863 | .size = sizeof(vector128), .align = sizeof(vector128), |
864 | .active = vr_active, .regset_get = vr_get, .set = vr_set |
865 | }, |
866 | #endif |
867 | #ifdef CONFIG_SPE |
868 | [REGSET_SPE] = { |
869 | .core_note_type = NT_PPC_SPE, .n = 35, |
870 | .size = sizeof(u32), .align = sizeof(u32), |
871 | .active = evr_active, .regset_get = evr_get, .set = evr_set |
872 | }, |
873 | #endif |
874 | #ifdef CONFIG_PPC_TRANSACTIONAL_MEM |
875 | [REGSET_TM_CGPR] = { |
876 | .core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG, |
877 | .size = sizeof(long), .align = sizeof(long), |
878 | .active = tm_cgpr_active, |
879 | .regset_get = tm_cgpr32_get, .set = tm_cgpr32_set |
880 | }, |
881 | [REGSET_TM_CFPR] = { |
882 | .core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG, |
883 | .size = sizeof(double), .align = sizeof(double), |
884 | .active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set |
885 | }, |
886 | [REGSET_TM_CVMX] = { |
887 | .core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX, |
888 | .size = sizeof(vector128), .align = sizeof(vector128), |
889 | .active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set |
890 | }, |
891 | [REGSET_TM_CVSX] = { |
892 | .core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX, |
893 | .size = sizeof(double), .align = sizeof(double), |
894 | .active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set |
895 | }, |
896 | [REGSET_TM_SPR] = { |
897 | .core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG, |
898 | .size = sizeof(u64), .align = sizeof(u64), |
899 | .active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set |
900 | }, |
901 | [REGSET_TM_CTAR] = { |
902 | .core_note_type = NT_PPC_TM_CTAR, .n = 1, |
903 | .size = sizeof(u64), .align = sizeof(u64), |
904 | .active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set |
905 | }, |
906 | [REGSET_TM_CPPR] = { |
907 | .core_note_type = NT_PPC_TM_CPPR, .n = 1, |
908 | .size = sizeof(u64), .align = sizeof(u64), |
909 | .active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set |
910 | }, |
911 | [REGSET_TM_CDSCR] = { |
912 | .core_note_type = NT_PPC_TM_CDSCR, .n = 1, |
913 | .size = sizeof(u64), .align = sizeof(u64), |
914 | .active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set |
915 | }, |
916 | #endif |
917 | #ifdef CONFIG_PPC64 |
918 | [REGSET_PPR] = { |
919 | .core_note_type = NT_PPC_PPR, .n = 1, |
920 | .size = sizeof(u64), .align = sizeof(u64), |
921 | .regset_get = ppr_get, .set = ppr_set |
922 | }, |
923 | [REGSET_DSCR] = { |
924 | .core_note_type = NT_PPC_DSCR, .n = 1, |
925 | .size = sizeof(u64), .align = sizeof(u64), |
926 | .regset_get = dscr_get, .set = dscr_set |
927 | }, |
928 | #endif |
929 | #ifdef CONFIG_PPC_BOOK3S_64 |
930 | [REGSET_TAR] = { |
931 | .core_note_type = NT_PPC_TAR, .n = 1, |
932 | .size = sizeof(u64), .align = sizeof(u64), |
933 | .regset_get = tar_get, .set = tar_set |
934 | }, |
935 | [REGSET_EBB] = { |
936 | .core_note_type = NT_PPC_EBB, .n = ELF_NEBB, |
937 | .size = sizeof(u64), .align = sizeof(u64), |
938 | .active = ebb_active, .regset_get = ebb_get, .set = ebb_set |
939 | }, |
940 | #endif |
941 | }; |
942 | |
943 | static const struct user_regset_view user_ppc_compat_view = { |
944 | .name = "ppc" , .e_machine = EM_PPC, .ei_osabi = ELF_OSABI, |
945 | .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) |
946 | }; |
947 | |
948 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
949 | { |
950 | if (IS_ENABLED(CONFIG_COMPAT) && is_tsk_32bit_task(task)) |
951 | return &user_ppc_compat_view; |
952 | return &user_ppc_native_view; |
953 | } |
954 | |