This patch defines and implements a new regset NT_ARM_SVE, which describes a thread's SVE register state. This allows a debugger to manipulate the SVE state, as well as being included in ELF coredumps for post-mortem debugging. Because the regset size and layout are dependent on the thread's current vector length, it is not possible to define a C struct to describe the regset contents as is done for existing regsets. Instead, and for the same reasons, NT_ARM_SVE is based on the freeform variable-layout approach used for the SVE signal frame. Additionally, to reduce debug overhead when debugging threads that might or might not have live SVE register state, NT_ARM_SVE may be presented in one of two different formats: the old struct user_fpsimd_state format is embedded for describing the state of a thread with no live SVE state, whereas a new variable-layout structure is embedded for describing live SVE state. This avoids a debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and allows existing userspace code to handle the non-SVE case without too much modification. For this to work, NT_ARM_SVE is defined with a fixed-format header of type struct user_sve_header, which the recipient can use to figure out the content, size and layout of the reset of the regset. Accessor macros are defined to allow the vector-length-dependent parts of the regset to be manipulated.
Unified · Split
| @@ -38,13 +38,16 @@ struct fpsimd_state { · 37 unmodified lines | ||
| 38 | 38 | __uint128_t vregs[32]; |
| 39 | 39 | u32 fpsr; |
| 40 | 40 | u32 fpcr; |
| 41 | + /* | |
| 42 | + * For ptrace compatibility, pad to next 128-bit | |
| 43 | + * boundary here if extending this struct. | |
| 44 | + */ | |
| 41 | 45 | }; |
| 42 | 46 | }; |
| 43 | 47 | /* the id of the last cpu to have restored this state */ |
| 44 | 48 | unsigned int cpu; |
| 45 | 49 | }; |
| 46 | 50 | |
| 47 | − | |
| 48 | 51 | #if defined(__KERNEL__) && defined(CONFIG_COMPAT) |
| 49 | 52 | /* Masks for extracting the FPSR and FPCR from the FPSCR */ |
| 50 | 53 | #define VFP_FPSCR_STAT_MASK 0xf800009f |
| @@ -88,6 +91,10 @@ extern size_t sve_state_size(struct task_struct const *task); · 37 unmodified lines | ||
| 88 | 91 | |
| 89 | 92 | extern void sve_alloc(struct task_struct *task); |
| 90 | 93 | extern void fpsimd_release_task(struct task_struct *task); |
| 94 | +extern void fpsimd_sync_to_sve(struct task_struct *task); | |
| 95 | +extern void sve_sync_to_fpsimd(struct task_struct *task); | |
| 96 | +extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task); | |
| 97 | + | |
| 91 | 98 | extern int sve_set_vector_length(struct task_struct *task, |
| 92 | 99 | unsigned long vl, unsigned long flags); |
| 93 | 100 | |
| @@ -104,6 +111,9 @@ extern void __init sve_setup(void); · 10 unmodified lines | ||
| 104 | 111 | |
| 105 | 112 | static inline void sve_alloc(struct task_struct *task) { } |
| 106 | 113 | static inline void fpsimd_release_task(struct task_struct *task) { } |
| 114 | +static inline void sve_sync_to_fpsimd(struct task_struct *task) { } | |
| 115 | +static inline void sve_sync_from_fpsimd_zeropad(struct task_struct *task) { } | |
| 116 | + | |
| 107 | 117 | static inline void sve_init_vq_map(void) { } |
| 108 | 118 | static inline void sve_update_vq_map(void) { } |
| 109 | 119 | static inline int sve_verify_vq_map(void) { return 0; } |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include <linux/types.h> |
| 23 | 23 | |
| 24 | 24 | #include <asm/hwcap.h> |
| 25 | +#include <asm/sigcontext.h> | |
| 25 | 26 | |
| 26 | 27 | |
| 27 | 28 | /* |
| @@ -62,6 +63,8 @@ · 34 unmodified lines | ||
| 62 | 63 | |
| 63 | 64 | #ifndef __ASSEMBLY__ |
| 64 | 65 | |
| 66 | +#include <linux/prctl.h> | |
| 67 | + | |
| 65 | 68 | /* |
| 66 | 69 | * User structures for general purpose, floating point and debug registers. |
| 67 | 70 | */ |
| @@ -89,6 +92,141 @@ struct user_hwdebug_state { · 21 unmodified lines | ||
| 89 | 92 | } dbg_regs[16]; |
| 90 | 93 | }; |
| 91 | 94 | |
| 95 | +/* SVE/FP/SIMD state (NT_ARM_SVE) */ | |
| 96 | + | |
| 97 | +struct user_sve_header { | |
| 98 | + __u32 size; /* total meaningful regset content in bytes */ | |
| 99 | + __u32 max_size; /* maxmium possible size for this thread */ | |
| 100 | + __u16 vl; /* current vector length */ | |
| 101 | + __u16 max_vl; /* maximum possible vector length */ | |
| 102 | + __u16 flags; | |
| 103 | + __u16 __reserved; | |
| 104 | +}; | |
| 105 | + | |
| 106 | +/* Definitions for user_sve_header.flags: */ | |
| 107 | +#define SVE_PT_REGS_MASK (1 << 0) | |
| 108 | + | |
| 109 | +#define SVE_PT_REGS_FPSIMD 0 | |
| 110 | +#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK | |
| 111 | + | |
| 112 | +/* | |
| 113 | + * Common SVE_PT_* flags: | |
| 114 | + * These must be kept in sync with prctl interface in <linux/ptrace.h> | |
| 115 | + */ | |
| 116 | +#define SVE_PT_VL_INHERIT (PR_SVE_VL_INHERIT >> 16) | |
| 117 | +#define SVE_PT_VL_ONEXEC (PR_SVE_SET_VL_ONEXEC >> 16) | |
| 118 | + | |
| 119 | + | |
| 120 | +/* | |
| 121 | + * The remainder of the SVE state follows struct user_sve_header. The | |
| 122 | + * total size of the SVE state (including header) depends on the | |
| 123 | + * metadata in the header: SVE_PT_SIZE(vq, flags) gives the total size | |
| 124 | + * of the state in bytes, including the header. | |
| 125 | + * | |
| 126 | + * Refer to <asm/sigcontext.h> for details of how to pass the correct | |
| 127 | + * "vq" argument to these macros. | |
| 128 | + */ | |
| 129 | + | |
| 130 | +/* Offset from the start of struct user_sve_header to the register data */ | |
| 131 | +#define SVE_PT_REGS_OFFSET \ | |
| 132 | + ((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \ | |
| 133 | + / SVE_VQ_BYTES * SVE_VQ_BYTES) | |
| 134 | + | |
| 135 | +/* | |
| 136 | + * The register data content and layout depends on the value of the | |
| 137 | + * flags field. | |
| 138 | + */ | |
| 139 | + | |
| 140 | +/* | |
| 141 | + * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD case: | |
| 142 | + * | |
| 143 | + * The payload starts at offset SVE_PT_FPSIMD_OFFSET, and is of type | |
| 144 | + * struct user_fpsimd_state. Additional data might be appended in the | |
| 145 | + * future: use SVE_PT_FPSIMD_SIZE(vq, flags) to compute the total size. | |
| 146 | + * SVE_PT_FPSIMD_SIZE(vq, flags) will never be less than | |
| 147 | + * sizeof(struct user_fpsimd_state). | |
| 148 | + */ | |
| 149 | + | |
| 150 | +#define SVE_PT_FPSIMD_OFFSET SVE_PT_REGS_OFFSET | |
| 151 | + | |
| 152 | +#define SVE_PT_FPSIMD_SIZE(vq, flags) (sizeof(struct user_fpsimd_state)) | |
| 153 | + | |
| 154 | +/* | |
| 155 | + * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE case: | |
| 156 | + * | |
| 157 | + * The payload starts at offset SVE_PT_SVE_OFFSET, and is of size | |
| 158 | + * SVE_PT_SVE_SIZE(vq, flags). | |
| 159 | + * | |
| 160 | + * Additional macros describe the contents and layout of the payload. | |
| 161 | + * For each, SVE_PT_SVE_x_OFFSET(args) is the start offset relative to | |
| 162 | + * the start of struct user_sve_header, and SVE_PT_SVE_x_SIZE(args) is | |
| 163 | + * the size in bytes: | |
| 164 | + * | |
| 165 | + * x type description | |
| 166 | + * - ---- ----------- | |
| 167 | + * ZREGS \ | |
| 168 | + * ZREG | | |
| 169 | + * PREGS | refer to <asm/sigcontext.h> | |
| 170 | + * PREG | | |
| 171 | + * FFR / | |
| 172 | + * | |
| 173 | + * FPSR uint32_t FPSR | |
| 174 | + * FPCR uint32_t FPCR | |
| 175 | + * | |
| 176 | + * Additional data might be appended in the future. | |
| 177 | + */ | |
| 178 | + | |
| 179 | +#define SVE_PT_SVE_ZREG_SIZE(vq) SVE_SIG_ZREG_SIZE(vq) | |
| 180 | +#define SVE_PT_SVE_PREG_SIZE(vq) SVE_SIG_PREG_SIZE(vq) | |
| 181 | +#define SVE_PT_SVE_FFR_SIZE(vq) SVE_SIG_FFR_SIZE(vq) | |
| 182 | +#define SVE_PT_SVE_FPSR_SIZE sizeof(__u32) | |
| 183 | +#define SVE_PT_SVE_FPCR_SIZE sizeof(__u32) | |
| 184 | + | |
| 185 | +#define __SVE_SIG_TO_PT(offset) \ | |
| 186 | + ((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET) | |
| 187 | + | |
| 188 | +#define SVE_PT_SVE_OFFSET SVE_PT_REGS_OFFSET | |
| 189 | + | |
| 190 | +#define SVE_PT_SVE_ZREGS_OFFSET \ | |
| 191 | + __SVE_SIG_TO_PT(SVE_SIG_ZREGS_OFFSET) | |
| 192 | +#define SVE_PT_SVE_ZREG_OFFSET(vq, n) \ | |
| 193 | + __SVE_SIG_TO_PT(SVE_SIG_ZREG_OFFSET(vq, n)) | |
| 194 | +#define SVE_PT_SVE_ZREGS_SIZE(vq) \ | |
| 195 | + (SVE_PT_SVE_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_PT_SVE_ZREGS_OFFSET) | |
| 196 | + | |
| 197 | +#define SVE_PT_SVE_PREGS_OFFSET(vq) \ | |
| 198 | + __SVE_SIG_TO_PT(SVE_SIG_PREGS_OFFSET(vq)) | |
| 199 | +#define SVE_PT_SVE_PREG_OFFSET(vq, n) \ | |
| 200 | + __SVE_SIG_TO_PT(SVE_SIG_PREG_OFFSET(vq, n)) | |
| 201 | +#define SVE_PT_SVE_PREGS_SIZE(vq) \ | |
| 202 | + (SVE_PT_SVE_PREG_OFFSET(vq, SVE_NUM_PREGS) - \ | |
| 203 | + SVE_PT_SVE_PREGS_OFFSET(vq)) | |
| 204 | + | |
| 205 | +#define SVE_PT_SVE_FFR_OFFSET(vq) \ | |
| 206 | + __SVE_SIG_TO_PT(SVE_SIG_FFR_OFFSET(vq)) | |
| 207 | + | |
| 208 | +#define SVE_PT_SVE_FPSR_OFFSET(vq) \ | |
| 209 | + ((SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq) + \ | |
| 210 | + (SVE_VQ_BYTES - 1)) \ | |
| 211 | + / SVE_VQ_BYTES * SVE_VQ_BYTES) | |
| 212 | +#define SVE_PT_SVE_FPCR_OFFSET(vq) \ | |
| 213 | + (SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE) | |
| 214 | + | |
| 215 | +/* | |
| 216 | + * Any future extension appended after FPCR must be aligned to the next | |
| 217 | + * 128-bit boundary. | |
| 218 | + */ | |
| 219 | + | |
| 220 | +#define SVE_PT_SVE_SIZE(vq, flags) \ | |
| 221 | + ((SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE \ | |
| 222 | + - SVE_PT_SVE_OFFSET + (SVE_VQ_BYTES - 1)) \ | |
| 223 | + / SVE_VQ_BYTES * SVE_VQ_BYTES) | |
| 224 | + | |
| 225 | +#define SVE_PT_SIZE(vq, flags) \ | |
| 226 | + (((flags) & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE ? \ | |
| 227 | + SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, flags) \ | |
| 228 | + : SVE_PT_FPSIMD_OFFSET + SVE_PT_FPSIMD_SIZE(vq, flags)) | |
| 229 | + | |
| 92 | 230 | #endif /* __ASSEMBLY__ */ |
| 93 | 231 | |
| 94 | 232 | #endif /* _UAPI__ASM_PTRACE_H */ |
| @@ -428,6 +428,66 @@ void sve_alloc(struct task_struct *task) · 427 unmodified lines | ||
| 428 | 428 | BUG_ON(!task->thread.sve_state); |
| 429 | 429 | } |
| 430 | 430 | |
| 431 | + | |
| 432 | +/* | |
| 433 | + * Ensure that task->thread.sve_state is up to date with respect to | |
| 434 | + * the user task, irrespective of when SVE is in use or not. | |
| 435 | + * | |
| 436 | + * This should only be called by ptrace. task must be non-runnable. | |
| 437 | + * task->thread.sve_state must point to at least sve_state_size(task) | |
| 438 | + * bytes of allocated kernel memory. | |
| 439 | + */ | |
| 440 | +void fpsimd_sync_to_sve(struct task_struct *task) | |
| 441 | +{ | |
| 442 | + if (!test_tsk_thread_flag(task, TIF_SVE)) | |
| 443 | + fpsimd_to_sve(task); | |
| 444 | +} | |
| 445 | + | |
| 446 | +/* | |
| 447 | + * Ensure that task->thread.fpsimd_state is up to date with respect to | |
| 448 | + * the user task, irrespective of whether SVE is in use or not. | |
| 449 | + * | |
| 450 | + * This should only be called by ptrace. task must be non-runnable. | |
| 451 | + * task->thread.sve_state must point to at least sve_state_size(task) | |
| 452 | + * bytes of allocated kernel memory. | |
| 453 | + */ | |
| 454 | +void sve_sync_to_fpsimd(struct task_struct *task) | |
| 455 | +{ | |
| 456 | + if (test_tsk_thread_flag(task, TIF_SVE)) | |
| 457 | + sve_to_fpsimd(task); | |
| 458 | +} | |
| 459 | + | |
| 460 | +/* | |
| 461 | + * Ensure that task->thread.sve_state is up to date with respect to | |
| 462 | + * the task->thread.fpsimd_state. | |
| 463 | + * | |
| 464 | + * This should only be called by ptrace to merge new FPSIMD register | |
| 465 | + * values into a task for which SVE is currently active. | |
| 466 | + * task must be non-runnable. | |
| 467 | + * task->thread.sve_state must point to at least sve_state_size(task) | |
| 468 | + * bytes of allocated kernel memory. | |
| 469 | + * task->thread.fpsimd_state must already have been initialised with | |
| 470 | + * the new FPSIMD register values to be merged in. | |
| 471 | + */ | |
| 472 | +void sve_sync_from_fpsimd_zeropad(struct task_struct *task) | |
| 473 | +{ | |
| 474 | + unsigned int vq; | |
| 475 | + void *sst = task->thread.sve_state; | |
| 476 | + struct fpsimd_state const *fst = &task->thread.fpsimd_state; | |
| 477 | + unsigned int i; | |
| 478 | + | |
| 479 | + if (!test_tsk_thread_flag(task, TIF_SVE)) | |
| 480 | + return; | |
| 481 | + | |
| 482 | + vq = sve_vq_from_vl(task->thread.sve_vl); | |
| 483 | + | |
| 484 | + memset(sst, 0, SVE_SIG_REGS_SIZE(vq)); | |
| 485 | + | |
| 486 | + for (i = 0; i < 32; ++i) | |
| 487 | + memcpy(ZREG(sst, vq, i), &fst->vregs[i], | |
| 488 | + sizeof(fst->vregs[i])); | |
| 489 | +} | |
| 490 | + | |
| 431 | 491 | int sve_set_vector_length(struct task_struct *task, |
| 432 | 492 | unsigned long vl, unsigned long flags) |
| 433 | 493 | { |
| @@ -32,6 +32,7 @@ · 31 unmodified lines | ||
| 32 | 32 | #include <linux/security.h> |
| 33 | 33 | #include <linux/init.h> |
| 34 | 34 | #include <linux/signal.h> |
| 35 | +#include <linux/string.h> | |
| 35 | 36 | #include <linux/uaccess.h> |
| 36 | 37 | #include <linux/perf_event.h> |
| 37 | 38 | #include <linux/hw_breakpoint.h> |
| @@ -40,6 +41,7 @@ · 2 unmodified lines | ||
| 40 | 41 | #include <linux/elf.h> |
| 41 | 42 | |
| 42 | 43 | #include <asm/compat.h> |
| 44 | +#include <asm/cpufeature.h> | |
| 43 | 45 | #include <asm/debug-monitors.h> |
| 44 | 46 | #include <asm/pgtable.h> |
| 45 | 47 | #include <asm/stacktrace.h> |
| @@ -618,17 +620,56 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, · 572 unmodified lines | ||
| 618 | 620 | /* |
| 619 | 621 | * TODO: update fp accessors for lazy context switching (sync/flush hwstate) |
| 620 | 622 | */ |
| 621 | −static int fpr_get(struct task_struct *target, const struct user_regset *regset, | |
| 622 | − unsigned int pos, unsigned int count, | |
| 623 | − void *kbuf, void __user *ubuf) | |
| 623 | +static int __fpr_get(struct task_struct *target, | |
| 624 | + const struct user_regset *regset, | |
| 625 | + unsigned int pos, unsigned int count, | |
| 626 | + void *kbuf, void __user *ubuf, unsigned int start_pos) | |
| 624 | 627 | { |
| 625 | 628 | struct user_fpsimd_state *uregs; |
| 629 | + | |
| 630 | + sve_sync_to_fpsimd(target); | |
| 631 | + | |
| 626 | 632 | uregs = &target->thread.fpsimd_state.user_fpsimd; |
| 627 | 633 | |
| 634 | + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, | |
| 635 | + start_pos, start_pos + sizeof(*uregs)); | |
| 636 | +} | |
| 637 | + | |
| 638 | +static int fpr_get(struct task_struct *target, const struct user_regset *regset, | |
| 639 | + unsigned int pos, unsigned int count, | |
| 640 | + void *kbuf, void __user *ubuf) | |
| 641 | +{ | |
| 628 | 642 | if (target == current) |
| 629 | 643 | fpsimd_preserve_current_state(); |
| 630 | 644 | |
| 631 | − return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); | |
| 645 | + return __fpr_get(target, regset, pos, count, kbuf, ubuf, 0); | |
| 646 | +} | |
| 647 | + | |
| 648 | +static int __fpr_set(struct task_struct *target, | |
| 649 | + const struct user_regset *regset, | |
| 650 | + unsigned int pos, unsigned int count, | |
| 651 | + const void *kbuf, const void __user *ubuf, | |
| 652 | + unsigned int start_pos) | |
| 653 | +{ | |
| 654 | + int ret; | |
| 655 | + struct user_fpsimd_state newstate; | |
| 656 | + | |
| 657 | + /* | |
| 658 | + * Ensure target->thread.fpsimd_state is up to date, so that a | |
| 659 | + * short copyin can't resurrect stale data. | |
| 660 | + */ | |
| 661 | + sve_sync_to_fpsimd(target); | |
| 662 | + | |
| 663 | + newstate = target->thread.fpsimd_state.user_fpsimd; | |
| 664 | + | |
| 665 | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, | |
| 666 | + start_pos, start_pos + sizeof(newstate)); | |
| 667 | + if (ret) | |
| 668 | + return ret; | |
| 669 | + | |
| 670 | + target->thread.fpsimd_state.user_fpsimd = newstate; | |
| 671 | + | |
| 672 | + return ret; | |
| 632 | 673 | } |
| 633 | 674 | |
| 634 | 675 | static int fpr_set(struct task_struct *target, const struct user_regset *regset, |
| @@ -636,15 +677,14 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, · 1 unmodified line | ||
| 636 | 677 | const void *kbuf, const void __user *ubuf) |
| 637 | 678 | { |
| 638 | 679 | int ret; |
| 639 | − struct user_fpsimd_state newstate = | |
| 640 | − target->thread.fpsimd_state.user_fpsimd; | |
| 641 | 680 | |
| 642 | − ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1); | |
| 681 | + ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, 0); | |
| 643 | 682 | if (ret) |
| 644 | 683 | return ret; |
| 645 | 684 | |
| 646 | − target->thread.fpsimd_state.user_fpsimd = newstate; | |
| 685 | + sve_sync_from_fpsimd_zeropad(target); | |
| 647 | 686 | fpsimd_flush_task_state(target); |
| 687 | + | |
| 648 | 688 | return ret; |
| 649 | 689 | } |
| 650 | 690 | |
| @@ -702,6 +742,215 @@ static int system_call_set(struct task_struct *target, · 51 unmodified lines | ||
| 702 | 742 | return ret; |
| 703 | 743 | } |
| 704 | 744 | |
| 745 | +#ifdef CONFIG_ARM64_SVE | |
| 746 | + | |
| 747 | +static void sve_init_header_from_task(struct user_sve_header *header, | |
| 748 | + struct task_struct *target) | |
| 749 | +{ | |
| 750 | + unsigned int vq; | |
| 751 | + | |
| 752 | + memset(header, 0, sizeof(*header)); | |
| 753 | + | |
| 754 | + header->flags = test_tsk_thread_flag(target, TIF_SVE) ? | |
| 755 | + SVE_PT_REGS_SVE : SVE_PT_REGS_FPSIMD; | |
| 756 | + if (test_tsk_thread_flag(target, TIF_SVE_VL_INHERIT)) | |
| 757 | + header->flags |= SVE_PT_VL_INHERIT; | |
| 758 | + | |
| 759 | + header->vl = target->thread.sve_vl; | |
| 760 | + vq = sve_vq_from_vl(header->vl); | |
| 761 | + | |
| 762 | + header->max_vl = sve_max_vl; | |
| 763 | + if (WARN_ON(!sve_vl_valid(sve_max_vl))) | |
| 764 | + header->max_vl = header->vl; | |
| 765 | + | |
| 766 | + header->size = SVE_PT_SIZE(vq, header->flags); | |
| 767 | + header->max_size = SVE_PT_SIZE(sve_vq_from_vl(header->max_vl), | |
| 768 | + SVE_PT_REGS_SVE); | |
| 769 | +} | |
| 770 | + | |
| 771 | +static unsigned int sve_size_from_header(struct user_sve_header const *header) | |
| 772 | +{ | |
| 773 | + return ALIGN(header->size, SVE_VQ_BYTES); | |
| 774 | +} | |
| 775 | + | |
| 776 | +static unsigned int sve_get_size(struct task_struct *target, | |
| 777 | + const struct user_regset *regset) | |
| 778 | +{ | |
| 779 | + struct user_sve_header header; | |
| 780 | + | |
| 781 | + if (!system_supports_sve()) | |
| 782 | + return 0; | |
| 783 | + | |
| 784 | + sve_init_header_from_task(&header, target); | |
| 785 | + return sve_size_from_header(&header); | |
| 786 | +} | |
| 787 | + | |
| 788 | +static int sve_get(struct task_struct *target, | |
| 789 | + const struct user_regset *regset, | |
| 790 | + unsigned int pos, unsigned int count, | |
| 791 | + void *kbuf, void __user *ubuf) | |
| 792 | +{ | |
| 793 | + int ret; | |
| 794 | + struct user_sve_header header; | |
| 795 | + unsigned int vq; | |
| 796 | + unsigned long start, end; | |
| 797 | + | |
| 798 | + if (!system_supports_sve()) | |
| 799 | + return -EINVAL; | |
| 800 | + | |
| 801 | + /* Header */ | |
| 802 | + sve_init_header_from_task(&header, target); | |
| 803 | + vq = sve_vq_from_vl(header.vl); | |
| 804 | + | |
| 805 | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &header, | |
| 806 | + 0, sizeof(header)); | |
| 807 | + if (ret) | |
| 808 | + return ret; | |
| 809 | + | |
| 810 | + if (target == current) | |
| 811 | + fpsimd_preserve_current_state(); | |
| 812 | + | |
| 813 | + /* Registers: FPSIMD-only case */ | |
| 814 | + | |
| 815 | + BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header)); | |
| 816 | + if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) | |
| 817 | + return __fpr_get(target, regset, pos, count, kbuf, ubuf, | |
| 818 | + SVE_PT_FPSIMD_OFFSET); | |
| 819 | + | |
| 820 | + /* Otherwise: full SVE case */ | |
| 821 | + | |
| 822 | + BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); | |
| 823 | + start = SVE_PT_SVE_OFFSET; | |
| 824 | + end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); | |
| 825 | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
| 826 | + target->thread.sve_state, | |
| 827 | + start, end); | |
| 828 | + if (ret) | |
| 829 | + return ret; | |
| 830 | + | |
| 831 | + start = end; | |
| 832 | + end = SVE_PT_SVE_FPSR_OFFSET(vq); | |
| 833 | + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
| 834 | + start, end); | |
| 835 | + if (ret) | |
| 836 | + return ret; | |
| 837 | + | |
| 838 | + /* | |
| 839 | + * Copy fpsr, and fpcr which must follow contiguously in | |
| 840 | + * struct fpsimd_state: | |
| 841 | + */ | |
| 842 | + start = end; | |
| 843 | + end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; | |
| 844 | + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
| 845 | + &target->thread.fpsimd_state.fpsr, | |
| 846 | + start, end); | |
| 847 | + if (ret) | |
| 848 | + return ret; | |
| 849 | + | |
| 850 | + start = end; | |
| 851 | + end = sve_size_from_header(&header); | |
| 852 | + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
| 853 | + start, end); | |
| 854 | +} | |
| 855 | + | |
| 856 | +static int sve_set(struct task_struct *target, | |
| 857 | + const struct user_regset *regset, | |
| 858 | + unsigned int pos, unsigned int count, | |
| 859 | + const void *kbuf, const void __user *ubuf) | |
| 860 | +{ | |
| 861 | + int ret; | |
| 862 | + struct user_sve_header header; | |
| 863 | + unsigned int vq; | |
| 864 | + unsigned long start, end; | |
| 865 | + | |
| 866 | + if (!system_supports_sve()) | |
| 867 | + return -EINVAL; | |
| 868 | + | |
| 869 | + /* Header */ | |
| 870 | + if (count < sizeof(header)) | |
| 871 | + return -EINVAL; | |
| 872 | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header, | |
| 873 | + 0, sizeof(header)); | |
| 874 | + if (ret) | |
| 875 | + goto out; | |
| 876 | + | |
| 877 | + /* | |
| 878 | + * Apart from PT_SVE_REGS_MASK, all PT_SVE_* flags are consumed by | |
| 879 | + * sve_set_vector_length(), which will also validate them for us: | |
| 880 | + */ | |
| 881 | + ret = sve_set_vector_length(target, header.vl, | |
| 882 | + ((unsigned long)header.flags & ~SVE_PT_REGS_MASK) << 16); | |
| 883 | + if (ret) | |
| 884 | + goto out; | |
| 885 | + | |
| 886 | + /* Actual VL set may be less than the user asked for: */ | |
| 887 | + vq = sve_vq_from_vl(target->thread.sve_vl); | |
| 888 | + | |
| 889 | + /* Registers: FPSIMD-only case */ | |
| 890 | + | |
| 891 | + BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header)); | |
| 892 | + if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) { | |
| 893 | + ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, | |
| 894 | + SVE_PT_FPSIMD_OFFSET); | |
| 895 | + clear_tsk_thread_flag(target, TIF_SVE); | |
| 896 | + goto out; | |
| 897 | + } | |
| 898 | + | |
| 899 | + /* Otherwise: full SVE case */ | |
| 900 | + | |
| 901 | + /* | |
| 902 | + * If setting a different VL from the requested VL and there is | |
| 903 | + * register data, the data layout will be wrong: don't even | |
| 904 | + * try to set the registers in this case. | |
| 905 | + */ | |
| 906 | + if (count && vq != sve_vq_from_vl(header.vl)) { | |
| 907 | + ret = -EIO; | |
| 908 | + goto out; | |
| 909 | + } | |
| 910 | + | |
| 911 | + sve_alloc(target); | |
| 912 | + | |
| 913 | + /* | |
| 914 | + * Ensure target->thread.sve_state is up to date with target's | |
| 915 | + * FPSIMD regs, so that a short copyin leaves trailing registers | |
| 916 | + * unmodified. | |
| 917 | + */ | |
| 918 | + fpsimd_sync_to_sve(target); | |
| 919 | + set_tsk_thread_flag(target, TIF_SVE); | |
| 920 | + | |
| 921 | + BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); | |
| 922 | + start = SVE_PT_SVE_OFFSET; | |
| 923 | + end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); | |
| 924 | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
| 925 | + target->thread.sve_state, | |
| 926 | + start, end); | |
| 927 | + if (ret) | |
| 928 | + goto out; | |
| 929 | + | |
| 930 | + start = end; | |
| 931 | + end = SVE_PT_SVE_FPSR_OFFSET(vq); | |
| 932 | + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
| 933 | + start, end); | |
| 934 | + if (ret) | |
| 935 | + goto out; | |
| 936 | + | |
| 937 | + /* | |
| 938 | + * Copy fpsr, and fpcr which must follow contiguously in | |
| 939 | + * struct fpsimd_state: | |
| 940 | + */ | |
| 941 | + start = end; | |
| 942 | + end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; | |
| 943 | + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
| 944 | + &target->thread.fpsimd_state.fpsr, | |
| 945 | + start, end); | |
| 946 | + | |
| 947 | +out: | |
| 948 | + fpsimd_flush_task_state(target); | |
| 949 | + return ret; | |
| 950 | +} | |
| 951 | + | |
| 952 | +#endif /* CONFIG_ARM64_SVE */ | |
| 953 | + | |
| 705 | 954 | enum aarch64_regset { |
| 706 | 955 | REGSET_GPR, |
| 707 | 956 | REGSET_FPR, |
| @@ -711,6 +960,9 @@ enum aarch64_regset { · 3 unmodified lines | ||
| 711 | 960 | REGSET_HW_WATCH, |
| 712 | 961 | #endif |
| 713 | 962 | REGSET_SYSTEM_CALL, |
| 963 | +#ifdef CONFIG_ARM64_SVE | |
| 964 | + REGSET_SVE, | |
| 965 | +#endif | |
| 714 | 966 | }; |
| 715 | 967 | |
| 716 | 968 | static const struct user_regset aarch64_regsets[] = { |
| @@ -768,6 +1020,18 @@ static const struct user_regset aarch64_regsets[] = { · 51 unmodified lines | ||
| 768 | 1020 | .get = system_call_get, |
| 769 | 1021 | .set = system_call_set, |
| 770 | 1022 | }, |
| 1023 | +#ifdef CONFIG_ARM64_SVE | |
| 1024 | + [REGSET_SVE] = { /* Scalable Vector Extension */ | |
| 1025 | + .core_note_type = NT_ARM_SVE, | |
| 1026 | + .n = DIV_ROUND_UP(SVE_PT_SIZE(SVE_VQ_MAX, SVE_PT_REGS_SVE), | |
| 1027 | + SVE_VQ_BYTES), | |
| 1028 | + .size = SVE_VQ_BYTES, | |
| 1029 | + .align = SVE_VQ_BYTES, | |
| 1030 | + .get = sve_get, | |
| 1031 | + .set = sve_set, | |
| 1032 | + .get_size = sve_get_size, | |
| 1033 | + }, | |
| 1034 | +#endif | |
| 771 | 1035 | }; |
| 772 | 1036 | |
| 773 | 1037 | static const struct user_regset_view user_aarch64_view = { |
| @@ -416,6 +416,7 @@ typedef struct elf64_shdr { · 415 unmodified lines | ||
| 416 | 416 | #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ |
| 417 | 417 | #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ |
| 418 | 418 | #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ |
| 419 | +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension registers */ | |
| 419 | 420 | #define NT_METAG_CBUF 0x500 /* Metag catch buffer registers */ |
| 420 | 421 | #define NT_METAG_RPIPE 0x501 /* Metag read pipeline state */ |
| 421 | 422 | #define NT_METAG_TLS 0x502 /* Metag TLS pointer */ |