git: aab60068943d - main - arm64: Support SVE in ptrace and core dumps
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 30 Sep 2024 12:23:39 UTC
The branch main has been updated by andrew: URL: https://cgit.FreeBSD.org/src/commit/?id=aab60068943d733b0b4573e5481c543ab3d45a00 commit aab60068943d733b0b4573e5481c543ab3d45a00 Author: Andrew Turner <andrew@FreeBSD.org> AuthorDate: 2024-09-27 13:37:17 +0000 Commit: Andrew Turner <andrew@FreeBSD.org> CommitDate: 2024-09-30 12:04:23 +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 --- 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 6cb92d1011fe..ec5bbbf9f0e6 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. */