Movatterモバイル変換


[0]ホーム

URL:


This is the mail archive of thelibc-alpha@sourceware.orgmailing list for theglibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav:[Date Prev] [Date Next][Thread Prev] [Thread Next]
Other format:[Raw text]

[PATCH 18/27] arm64/sve: ptrace and ELF coredump support


This patch defines and implements a new regset NT_ARM_SVE, whichdescribes a thread's SVE register state.  This allows a debugger tomanipulate the SVE state, as well as being included in ELFcoredumps for post-mortem debugging.Because the regset size and layout are dependent on the thread'scurrent vector length, it is not possible to define a C struct todescribe the regset contents as is done for existing regsets.Instead, and for the same reasons, NT_ARM_SVE is based on thefreeform variable-layout approach used for the SVE signal frame.Additionally, to reduce debug overhead when debugging threads thatmight or might not have live SVE register state, NT_ARM_SVE may bepresented in one of two different formats: the old structuser_fpsimd_state format is embedded for describing the state of athread with no live SVE state, whereas a new variable-layoutstructure is embedded for describing live SVE state.  This avoids adebugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, andallows existing userspace code to handle the non-SVE case withouttoo much modification.For this to work, NT_ARM_SVE is defined with a fixed-format headerof type struct user_sve_header, which the recipient can use tofigure out the content, size and layout of the reset of the regset.Accessor macros are defined to allow the vector-length-dependentparts of the regset to be manipulated.Signed-off-by: Alan Hayward <alan.hayward@arm.com>Signed-off-by: Dave Martin <Dave.Martin@arm.com>--- arch/arm64/include/asm/fpsimd.h      |  13 +- arch/arm64/include/uapi/asm/ptrace.h | 130 ++++++++++++++++ arch/arm64/kernel/fpsimd.c           |  34 +++++ arch/arm64/kernel/ptrace.c           | 288 ++++++++++++++++++++++++++++++++++- include/uapi/linux/elf.h             |   1 + 5 files changed, 457 insertions(+), 9 deletions(-)diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.hindex f43f573..ffb8a50 100644--- a/arch/arm64/include/asm/fpsimd.h+++ b/arch/arm64/include/asm/fpsimd.h@@ -37,13 +37,16 @@ struct fpsimd_state { __uint128_t vregs[32]; u32 fpsr; u32 fpcr;+/*+ * For ptrace compatibility, pad to next 128-bit+ * boundary here if extending this struct.+ */ }; }; /* the id of the last cpu to have restored this state */ unsigned int cpu; }; - #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* Masks for extracting the FPSR and FPCR from the FPSCR */ #define VFP_FPSCR_STAT_MASK0xf800009f@@ -87,6 +90,10 @@ extern void sve_alloc(struct task_struct *task); extern void fpsimd_release_thread(struct task_struct *task); extern void fpsimd_dup_sve(struct task_struct *dst,    struct task_struct const *src);+extern void fpsimd_sync_to_sve(struct task_struct *task);+extern void sve_sync_to_fpsimd(struct task_struct *task);+extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task);+ extern int sve_set_vector_length(struct task_struct *task,  unsigned long vl, unsigned long flags); @@ -101,6 +108,10 @@ static void __maybe_unused sve_alloc(struct task_struct *task) { } static void __maybe_unused fpsimd_release_thread(struct task_struct *task) { } static void __maybe_unused fpsimd_dup_sve(struct task_struct *dst,   struct task_struct const *src) { }+static void __maybe_unused sve_sync_to_fpsimd(struct task_struct *task) { }+static void __maybe_unused sve_sync_from_fpsimd_zeropad(+struct task_struct *task) { }+ static void __maybe_unused sve_init_vq_map(void) { } static void __maybe_unused sve_update_vq_map(void) { } static int __maybe_unused sve_verify_vq_map(void) { return 0; }diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.hindex d1ff83d..16d9344 100644--- a/arch/arm64/include/uapi/asm/ptrace.h+++ b/arch/arm64/include/uapi/asm/ptrace.h@@ -22,6 +22,7 @@ #include <linux/types.h>  #include <asm/hwcap.h>+#include <asm/sigcontext.h>   /*@@ -63,6 +64,8 @@  #ifndef __ASSEMBLY__ +#include <linux/prctl.h>+ /*  * User structures for general purpose, floating point and debug registers.  */@@ -90,6 +93,133 @@ struct user_hwdebug_state { }dbg_regs[16]; }; +/* SVE/FP/SIMD state (NT_ARM_SVE) */++struct user_sve_header {+__u32 size; /* total meaningful regset content in bytes */+__u32 max_size; /* maxmium possible size for this thread */+__u16 vl; /* current vector length */+__u16 max_vl; /* maximum possible vector length */+__u16 flags;+__u16 __reserved;+};++/* Definitions for user_sve_header.flags: */+#define SVE_PT_REGS_MASK(1 << 0)++/* Flags: must be kept in sync with prctl interface in <linux/ptrace.h> */+#define SVE_PT_REGS_FPSIMD0+#define SVE_PT_REGS_SVESVE_PT_REGS_MASK++#define SVE_PT_VL_INHERIT(PR_SVE_VL_INHERIT >> 16)+#define SVE_PT_VL_ONEXEC(PR_SVE_SET_VL_ONEXEC >> 16)+++/*+ * The remainder of the SVE state follows struct user_sve_header.  The+ * total size of the SVE state (including header) depends on the+ * metadata in the header:  SVE_PT_SIZE(vq, flags) gives the total size+ * of the state in bytes, including the header.+ *+ * Refer to <asm/sigcontext.h> for details of how to pass the correct+ * "vq" argument to these macros.+ */++/* Offset from the start of struct user_sve_header to the register data */+#define SVE_PT_REGS_OFFSET((sizeof(struct sve_context) + 15) / 16 * 16)++/*+ * The register data content and layout depends on the value of the+ * flags field.+ */++/*+ * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD case:+ *+ * The payload starts at offset SVE_PT_FPSIMD_OFFSET, and is of type+ * struct user_fpsimd_state.  Additional data might be appended in the+ * future: use SVE_PT_FPSIMD_SIZE(vq, flags) to compute the total size.+ * SVE_PT_FPSIMD_SIZE(vq, flags) will never be less than+ * sizeof(struct user_fpsimd_state).+ */++#define SVE_PT_FPSIMD_OFFSETSVE_PT_REGS_OFFSET++#define SVE_PT_FPSIMD_SIZE(vq, flags)(sizeof(struct user_fpsimd_state))++/*+ * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE case:+ *+ * The payload starts at offset SVE_PT_SVE_OFFSET, and is of size+ * SVE_PT_SVE_SIZE(vq, flags).+ *+ * Additional macros describe the contents and layout of the payload.+ * For each, SVE_PT_SVE_x_OFFSET(args) is the start offset relative to+ * the start of struct user_sve_header, and SVE_PT_SVE_x_SIZE(args) is+ * the size in bytes:+ *+ *xtypedescription+ *----------------+ *ZREGS\+ *ZREG|+ *PREGS| refer to <asm/sigcontext.h>+ *PREG|+ *FFR/+ *+ *FPSRuint32_tFPSR+ *FPCRuint32_tFPCR+ *+ * Additional data might be appended in the future.+ */++#define SVE_PT_SVE_ZREG_SIZE(vq)SVE_SIG_ZREG_SIZE(vq)+#define SVE_PT_SVE_PREG_SIZE(vq)SVE_SIG_PREG_SIZE(vq)+#define SVE_PT_SVE_FFR_SIZE(vq)SVE_SIG_FFR_SIZE(vq)+#define SVE_PT_SVE_FPSR_SIZEsizeof(__u32)+#define SVE_PT_SVE_FPCR_SIZEsizeof(__u32)++#define __SVE_SIG_TO_PT(offset) \+((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET)++#define SVE_PT_SVE_OFFSETSVE_PT_REGS_OFFSET++#define SVE_PT_SVE_ZREGS_OFFSET \+__SVE_SIG_TO_PT(SVE_SIG_ZREGS_OFFSET)+#define SVE_PT_SVE_ZREG_OFFSET(vq, n) \+__SVE_SIG_TO_PT(SVE_SIG_ZREG_OFFSET(vq, n))+#define SVE_PT_SVE_ZREGS_SIZE(vq) \+(SVE_PT_SVE_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_PT_SVE_ZREGS_OFFSET)++#define SVE_PT_SVE_PREGS_OFFSET(vq) \+__SVE_SIG_TO_PT(SVE_SIG_PREGS_OFFSET(vq))+#define SVE_PT_SVE_PREG_OFFSET(vq, n) \+__SVE_SIG_TO_PT(SVE_SIG_PREG_OFFSET(vq, n))+#define SVE_PT_SVE_PREGS_SIZE(vq) \+(SVE_PT_SVE_PREG_OFFSET(vq, SVE_NUM_PREGS) - \+SVE_PT_SVE_PREGS_OFFSET(vq))++#define SVE_PT_SVE_FFR_OFFSET(vq) \+__SVE_SIG_TO_PT(SVE_SIG_FFR_OFFSET(vq))++#define SVE_PT_SVE_FPSR_OFFSET(vq) \+((SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq) + 15) / 16 * 16)+#define SVE_PT_SVE_FPCR_OFFSET(vq) \+(SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE)++/*+ * Any future extension appended after FPCR must be aligned to the next+ * 128-bit boundary.+ */++#define SVE_PT_SVE_SIZE(vq, flags)\+((SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE -\+SVE_PT_SVE_OFFSET + 15) / 16 * 16)++#define SVE_PT_SIZE(vq, flags)\+ (((flags) & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE ?\+  SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, flags)\+: SVE_PT_FPSIMD_OFFSET + SVE_PT_FPSIMD_SIZE(vq, flags))+ #endif /* __ASSEMBLY__ */  #endif /* _UAPI__ASM_PTRACE_H */diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.cindex c727b47..5a7c870 100644--- a/arch/arm64/kernel/fpsimd.c+++ b/arch/arm64/kernel/fpsimd.c@@ -268,6 +268,40 @@ void sve_alloc(struct task_struct *task) BUG_ON(!task->thread.sve_state); } +void fpsimd_sync_to_sve(struct task_struct *task)+{+if (!test_tsk_thread_flag(task, TIF_SVE))+fpsimd_to_sve(task);+}++void sve_sync_to_fpsimd(struct task_struct *task)+{+if (test_tsk_thread_flag(task, TIF_SVE))+sve_to_fpsimd(task);+}++void sve_sync_from_fpsimd_zeropad(struct task_struct *task)+{+unsigned int vl, vq;+void *sst = task->thread.sve_state;+struct fpsimd_state const *fst = &task->thread.fpsimd_state;+unsigned int i;++if (!test_tsk_thread_flag(task, TIF_SVE))+return;++vl = task->thread.sve_vl;++BUG_ON(!sve_vl_valid(vl));+vq = sve_vq_from_vl(vl);++memset(sst, 0, SVE_SIG_REGS_SIZE(vq));++for (i = 0; i < 32; ++i)+memcpy(ZREG(sst, vq, i), &fst->vregs[i],+       sizeof(fst->vregs[i]));+}+ /*  * Handle SVE state across fork():  *diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.cindex 28619b5..abc05ed 100644--- a/arch/arm64/kernel/ptrace.c+++ b/arch/arm64/kernel/ptrace.c@@ -32,6 +32,7 @@ #include <linux/security.h> #include <linux/init.h> #include <linux/signal.h>+#include <linux/string.h> #include <linux/uaccess.h> #include <linux/perf_event.h> #include <linux/hw_breakpoint.h>@@ -40,6 +41,7 @@ #include <linux/elf.h>  #include <asm/compat.h>+#include <asm/cpufeature.h> #include <asm/debug-monitors.h> #include <asm/pgtable.h> #include <asm/syscall.h>@@ -617,33 +619,66 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, /*  * TODO: update fp accessors for lazy context switching (sync/flush hwstate)  */-static int fpr_get(struct task_struct *target, const struct user_regset *regset,-   unsigned int pos, unsigned int count,-   void *kbuf, void __user *ubuf)+static int __fpr_get(struct task_struct *target,+     const struct user_regset *regset,+     unsigned int pos, unsigned int count,+     void *kbuf, void __user *ubuf, unsigned int start_pos) { struct user_fpsimd_state *uregs;++sve_sync_to_fpsimd(target);+ uregs = &target->thread.fpsimd_state.user_fpsimd; +return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs,+   start_pos, start_pos + sizeof(*uregs));+}++static int fpr_get(struct task_struct *target, const struct user_regset *regset,+   unsigned int pos, unsigned int count,+   void *kbuf, void __user *ubuf)+{ if (target == current) fpsimd_preserve_current_state(); -return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1);+return __fpr_get(target, regset, pos, count, kbuf, ubuf, 0); } -static int fpr_set(struct task_struct *target, const struct user_regset *regset,-   unsigned int pos, unsigned int count,-   const void *kbuf, const void __user *ubuf)+static int __fpr_set(struct task_struct *target,+     const struct user_regset *regset,+     unsigned int pos, unsigned int count,+     const void *kbuf, const void __user *ubuf,+     unsigned int start_pos) { int ret; struct user_fpsimd_state newstate = target->thread.fpsimd_state.user_fpsimd; -ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);+sve_sync_to_fpsimd(target);++ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate,+ start_pos, start_pos + sizeof(newstate)); if (ret) return ret;  target->thread.fpsimd_state.user_fpsimd = newstate;++return ret;+}++static int fpr_set(struct task_struct *target, const struct user_regset *regset,+   unsigned int pos, unsigned int count,+   const void *kbuf, const void __user *ubuf)+{+int ret;++ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, 0);+if (ret)+return ret;++sve_sync_from_fpsimd_zeropad(target); fpsimd_flush_task_state(target);+ return ret; } @@ -701,6 +736,229 @@ static int system_call_set(struct task_struct *target, return ret; } +#ifdef CONFIG_ARM64_SVE++static void sve_init_header_from_task(struct user_sve_header *header,+      struct task_struct *target)+{+unsigned int vq;++memset(header, 0, sizeof(*header));++header->flags = test_tsk_thread_flag(target, TIF_SVE) ?+SVE_PT_REGS_SVE : SVE_PT_REGS_FPSIMD;+if (test_tsk_thread_flag(target, TIF_SVE_VL_INHERIT))+header->flags |= SVE_PT_VL_INHERIT;++header->vl = target->thread.sve_vl;++BUG_ON(!sve_vl_valid(header->vl));+vq = sve_vq_from_vl(header->vl);++BUG_ON(!sve_vl_valid(sve_max_vl));+header->max_vl = sve_max_vl;++header->size = SVE_PT_SIZE(vq, header->flags);+header->max_size = SVE_PT_SIZE(sve_vq_from_vl(header->max_vl),+      SVE_PT_REGS_SVE);+}++static unsigned int sve_size_from_header(struct user_sve_header const *header)+{+return (header->size + 15) / 16 * 16;+}++static unsigned int sve_get_size(struct task_struct *target,+ const struct user_regset *regset)+{+struct user_sve_header header;++if (!system_supports_sve())+return 0;++sve_init_header_from_task(&header, target);+return sve_size_from_header(&header);+}++static int sve_get(struct task_struct *target,+   const struct user_regset *regset,+   unsigned int pos, unsigned int count,+   void *kbuf, void __user *ubuf)+{+int ret;+struct user_sve_header header;+unsigned int vq;+unsigned long start, end;++if (!system_supports_sve())+return -EINVAL;++/* Header */+sve_init_header_from_task(&header, target);++BUG_ON(!sve_vl_valid(header.vl));+vq = sve_vq_from_vl(header.vl);++ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &header,+  0, sizeof(header));+if (ret)+return ret;++if (target == current)+fpsimd_preserve_current_state();++/* Registers: FPSIMD-only case */++BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header));++if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD)+return __fpr_get(target, regset, pos, count, kbuf, ubuf,+ SVE_PT_FPSIMD_OFFSET);++/* Otherwise: full SVE case */++BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header));++start = SVE_PT_SVE_OFFSET;+end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq);++BUG_ON(end < start);+BUG_ON(end - start > sve_state_size(target));+ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,+  target->thread.sve_state,+  start, end);+if (ret)+return ret;++start = end;+end = SVE_PT_SVE_FPSR_OFFSET(vq);++BUG_ON(end < start);+ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,+       start, end);+if (ret)+return ret;++start = end;+end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE;++BUG_ON((char *)(&target->thread.fpsimd_state.fpcr + 1) <+       (char *)&target->thread.fpsimd_state.fpsr);+BUG_ON(end < start);+BUG_ON((char *)(&target->thread.fpsimd_state.fpcr + 1) -+       (char *)&target->thread.fpsimd_state.fpsr !=+end - start);++ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,+  &target->thread.fpsimd_state.fpsr,+  start, end);+if (ret)+return ret;++start = end;+end = sve_size_from_header(&header);+BUG_ON(end < start);++return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,+start, end);+}++static int sve_set(struct task_struct *target,+   const struct user_regset *regset,+   unsigned int pos, unsigned int count,+   const void *kbuf, const void __user *ubuf)+{+int ret;+struct user_sve_header header;+unsigned int vq;+unsigned long start, end;++if (!system_supports_sve())+return -EINVAL;++/* Header */+if (count < sizeof(header))+return -EINVAL;+ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header,+ 0, sizeof(header));+if (ret)+goto out;++/*+ * Apart from PT_SVE_REGS_MASK, all PT_SVE_* flags are consumed by+ * sve_set_vector_length(), which will also validate them for us:+ */+ret = sve_set_vector_length(target, header.vl,+    header.flags & ~SVE_PT_REGS_MASK);+if (ret)+goto out;++/* Actual VL set may be less than the user asked for: */+BUG_ON(!sve_vl_valid(target->thread.sve_vl));+vq = sve_vq_from_vl(target->thread.sve_vl);++/* Registers: FPSIMD-only case */++BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header));++if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) {+sve_sync_to_fpsimd(target);++ret = __fpr_set(target, regset, pos, count, kbuf, ubuf,+SVE_PT_FPSIMD_OFFSET);+clear_tsk_thread_flag(target, TIF_SVE);+goto out;+}++/* Otherwise: full SVE case */++sve_alloc(target);+fpsimd_sync_to_sve(target);+set_tsk_thread_flag(target, TIF_SVE);++BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header));++start = SVE_PT_SVE_OFFSET;+end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq);++BUG_ON(end < start);+BUG_ON(end - start > sve_state_size(target));+ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,+ target->thread.sve_state,+ start, end);+if (ret)+goto out;++start = end;+end = SVE_PT_SVE_FPSR_OFFSET(vq);++BUG_ON(end < start);+ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,+start, end);+if (ret)+goto out;++start = end;+end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE;++BUG_ON((char *)(&target->thread.fpsimd_state.fpcr + 1) <+(char *)&target->thread.fpsimd_state.fpsr);+BUG_ON(end < start);+BUG_ON((char *)(&target->thread.fpsimd_state.fpcr + 1) -+       (char *)&target->thread.fpsimd_state.fpsr !=+end - start);++ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,+ &target->thread.fpsimd_state.fpsr,+ start, end);++out:+fpsimd_flush_task_state(target);+return ret;+}++#endif /* CONFIG_ARM64_SVE */+ enum aarch64_regset { REGSET_GPR, REGSET_FPR,@@ -710,6 +968,9 @@ enum aarch64_regset { REGSET_HW_WATCH, #endif REGSET_SYSTEM_CALL,+#ifdef CONFIG_ARM64_SVE+REGSET_SVE,+#endif };  static const struct user_regset aarch64_regsets[] = {@@ -767,6 +1028,17 @@ static const struct user_regset aarch64_regsets[] = { .get = system_call_get, .set = system_call_set, },+#ifdef CONFIG_ARM64_SVE+[REGSET_SVE] = { /* Scalable Vector Extension */+.core_note_type = NT_ARM_SVE,+.n = (SVE_PT_SIZE(SVE_VQ_MAX, SVE_PT_REGS_SVE) + 15) / 16,+.size = 16,+.align = 16,+.get = sve_get,+.set = sve_set,+.get_size = sve_get_size,+},+#endif };  static const struct user_regset_view user_aarch64_view = {diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.hindex b5280db..735b8f4 100644--- a/include/uapi/linux/elf.h+++ b/include/uapi/linux/elf.h@@ -416,6 +416,7 @@ typedef struct elf64_shdr { #define NT_ARM_HW_BREAK0x402/* ARM hardware breakpoint registers */ #define NT_ARM_HW_WATCH0x403/* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL0x404/* ARM system call number */+#define NT_ARM_SVE0x405/* ARM Scalable Vector Extension registers */ #define NT_METAG_CBUF0x500/* Metag catch buffer registers */ #define NT_METAG_RPIPE0x501/* Metag read pipeline state */ #define NT_METAG_TLS0x502/* Metag TLS pointer */-- 2.1.4

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav:[Date Prev] [Date Next][Thread Prev] [Thread Next]

[8]ページ先頭

©2009-2026 Movatter.jp