The branch stable/14 has been updated by andrew:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=f7caa6fb7489b7e9721f064e39050614ae0fc875

commit f7caa6fb7489b7e9721f064e39050614ae0fc875
Author:     Andrew Turner <and...@freebsd.org>
AuthorDate: 2024-09-27 13:37:17 +0000
Commit:     Andrew Turner <and...@freebsd.org>
CommitDate: 2024-10-21 15:03:27 +0000

    arm64: Support SVE in ptrace and core dumps
    
    Add the NT_ARM_SVE note type and use it to access the SVE registers
    from ptrace. This allows userspace to modify the full SVE register
    values.
    
    Try to follow the Linux semantics to allow debuggers to use this with
    minimal changes.
    
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D43309
    
    (cherry picked from commit aab60068943d733b0b4573e5481c543ab3d45a00)
---
 sys/arm64/arm64/vfp.c   | 141 ++++++++++++++++++++++++++++++++++++++++++++++++
 sys/arm64/include/reg.h |  13 +++++
 sys/sys/elf_common.h    |   1 +
 3 files changed, 155 insertions(+)

diff --git a/sys/arm64/arm64/vfp.c b/sys/arm64/arm64/vfp.c
index d57927991c03..a3aa77ed6180 100644
--- a/sys/arm64/arm64/vfp.c
+++ b/sys/arm64/arm64/vfp.c
@@ -30,12 +30,14 @@
 #ifdef VFP
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/elf.h>
 #include <sys/eventhandler.h>
 #include <sys/limits.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/pcpu.h>
 #include <sys/proc.h>
+#include <sys/reg.h>
 #include <sys/smp.h>
 
 #include <vm/uma.h>
@@ -918,6 +920,145 @@ sve_init(const void *dummy __unused)
 }
 SYSINIT(sve, SI_SUB_SMP, SI_ORDER_ANY, sve_init, NULL);
 
+static bool
+get_arm64_sve(struct regset *rs, struct thread *td, void *buf,
+    size_t *sizep)
+{
+       struct svereg_header *header;
+       struct pcb *pcb;
+       size_t buf_size;
+       uint16_t sve_flags;
+
+       pcb = td->td_pcb;
+
+       /* If there is no SVE support in HW then we don't support NT_ARM_SVE */
+       if (pcb->pcb_sve_len == 0)
+               return (false);
+
+       sve_flags = 0;
+       if ((pcb->pcb_fpflags & PCB_FP_SVEVALID) == 0) {
+               /* If SVE hasn't been used yet provide the VFP registers */
+               buf_size = sizeof(struct fpreg);
+               sve_flags |= SVEREG_FLAG_FP;
+       } else {
+               /* We have SVE registers */
+               buf_size = sve_buf_size(td);
+               sve_flags |= SVEREG_FLAG_SVE;
+               KASSERT(pcb->pcb_svesaved != NULL, ("%s: no saved sve",
+                   __func__));
+       }
+
+       if (buf != NULL) {
+               KASSERT(*sizep == sizeof(struct svereg_header) + buf_size,
+                   ("%s: invalid size", __func__));
+
+               if (td == curthread && (pcb->pcb_fpflags & PCB_FP_STARTED) != 0)
+                       vfp_save_state(td, pcb);
+
+               header = buf;
+               memset(header, 0, sizeof(*header));
+
+               header->sve_size = sizeof(struct svereg_header) + buf_size;
+               header->sve_maxsize = sizeof(struct svereg_header) +
+                   sve_max_buf_size();
+               header->sve_vec_len = pcb->pcb_sve_len;
+               header->sve_max_vec_len = sve_max_vector_len;
+               header->sve_flags = sve_flags;
+
+               if ((sve_flags & SVEREG_FLAG_REGS_MASK) == SVEREG_FLAG_FP) {
+                       struct fpreg *fpregs;
+
+                       fpregs = (void *)(&header[1]);
+                       memcpy(fpregs->fp_q, pcb->pcb_fpustate.vfp_regs,
+                           sizeof(fpregs->fp_q));
+                       fpregs->fp_cr = pcb->pcb_fpustate.vfp_fpcr;
+                       fpregs->fp_sr = pcb->pcb_fpustate.vfp_fpsr;
+               } else {
+                       memcpy((void *)(&header[1]), pcb->pcb_svesaved,
+                           buf_size);
+               }
+       }
+       *sizep = sizeof(struct svereg_header) + buf_size;
+
+       return (true);
+}
+
+static bool
+set_arm64_sve(struct regset *rs, struct thread *td, void *buf, size_t size)
+{
+       struct svereg_header *header;
+       struct pcb *pcb;
+       size_t buf_size;
+       uint16_t sve_flags;
+
+       pcb = td->td_pcb;
+
+       /* If there is no SVE support in HW then we don't support NT_ARM_SVE */
+       if (pcb->pcb_sve_len == 0)
+               return (false);
+
+       sve_flags = 0;
+       if ((pcb->pcb_fpflags & PCB_FP_SVEVALID) == 0) {
+               /*
+                * If the SVE state is invalid it provide the FP registers.
+                * This may be beause it hasn't been used, or it has but
+                * was switched out in a system call.
+                */
+               buf_size = sizeof(struct fpreg);
+               sve_flags |= SVEREG_FLAG_FP;
+       } else {
+               /* We have SVE registers */
+               MPASS(pcb->pcb_svesaved != NULL);
+               buf_size = sve_buf_size(td);
+               sve_flags |= SVEREG_FLAG_SVE;
+               KASSERT(pcb->pcb_svesaved != NULL, ("%s: no saved sve",
+                   __func__));
+       }
+
+       if (size != sizeof(struct svereg_header) + buf_size)
+               return (false);
+
+       header = buf;
+       /* Sanity checks on the header */
+       if (header->sve_size != sizeof(struct svereg_header) + buf_size)
+               return (false);
+
+       if (header->sve_maxsize != sizeof(struct svereg_header) +
+           sve_max_buf_size())
+               return (false);
+
+       if (header->sve_vec_len != pcb->pcb_sve_len)
+               return (false);
+
+       if (header->sve_max_vec_len != sve_max_vector_len)
+               return (false);
+
+       if (header->sve_flags != sve_flags)
+               return (false);
+
+       if ((sve_flags & SVEREG_FLAG_REGS_MASK) == SVEREG_FLAG_FP) {
+               struct fpreg *fpregs;
+
+               fpregs = (void *)(&header[1]);
+               memcpy(pcb->pcb_fpustate.vfp_regs, fpregs->fp_q,
+                   sizeof(fpregs->fp_q));
+               pcb->pcb_fpustate.vfp_fpcr = fpregs->fp_cr;
+               pcb->pcb_fpustate.vfp_fpsr = fpregs->fp_sr;
+       } else {
+               /* Restore the SVE registers */
+               memcpy(pcb->pcb_svesaved, (void *)(&header[1]), buf_size);
+       }
+
+       return (true);
+}
+
+static struct regset regset_arm64_sve = {
+       .note = NT_ARM_SVE,
+       .get = get_arm64_sve,
+       .set = set_arm64_sve,
+};
+ELF_REGSET(regset_arm64_sve);
+
 struct fpu_kern_ctx *
 fpu_kern_alloc_ctx(u_int flags)
 {
diff --git a/sys/arm64/include/reg.h b/sys/arm64/include/reg.h
index c699752197a8..4226385480e8 100644
--- a/sys/arm64/include/reg.h
+++ b/sys/arm64/include/reg.h
@@ -63,6 +63,19 @@ struct fpreg32 {
        int dummy;
 };
 
+#define        SVEREG_FLAG_REGS_MASK   0x0001
+#define        SVEREG_FLAG_FP          0x0000
+#define        SVEREG_FLAG_SVE         0x0001
+
+struct svereg_header {
+       __uint32_t      sve_size;
+       __uint32_t      sve_maxsize;
+       __uint16_t      sve_vec_len;
+       __uint16_t      sve_max_vec_len;
+       __uint16_t      sve_flags;
+       __uint16_t      sve_reserved;
+};
+
 struct dbreg {
        __uint8_t       db_debug_ver;
        __uint8_t       db_nbkpts;
diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h
index b9e82cb8fa35..0c7d7e2b2fc3 100644
--- a/sys/sys/elf_common.h
+++ b/sys/sys/elf_common.h
@@ -826,6 +826,7 @@ typedef struct {
 #define        NT_X86_XSTATE   0x202   /* x86 XSAVE extended state. */
 #define        NT_ARM_VFP      0x400   /* ARM VFP registers */
 #define        NT_ARM_TLS      0x401   /* ARM TLS register */
+#define        NT_ARM_SVE      0x405   /* ARM SVE registers */
 #define        NT_ARM_ADDR_MASK        0x406   /* arm64 address mask (e.g. for 
TBI) */
 
 /* GNU note types. */

Reply via email to