From 6ada909eaf5ebfbd7d8c5839bab521cb9525c94a Mon Sep 17 00:00:00 2001 From: Luis Machado Date: Mon, 6 Feb 2023 17:24:32 +0000 Subject: refactor: Rename SVE-specific files In preparation to the SME support patches, rename the SVE-specific files to something a bit more meaningful that can be shared with the SME code. In this case, I've renamed the "sve" in the names to "scalable". No functional changes. Regression-tested on aarch64-linux Ubuntu 22.04/20.04. Reviewed-by: Thiago Jung Bauermann --- gdb/Makefile.in | 2 +- gdb/aarch64-linux-nat.c | 2 +- gdb/configure.nat | 2 +- gdb/nat/aarch64-scalable-linux-ptrace.c | 393 ++++++++++++++++++++++++++++ gdb/nat/aarch64-scalable-linux-ptrace.h | 73 ++++++ gdb/nat/aarch64-scalable-linux-sigcontext.h | 270 +++++++++++++++++++ gdb/nat/aarch64-sve-linux-ptrace.c | 392 --------------------------- gdb/nat/aarch64-sve-linux-ptrace.h | 72 ----- gdb/nat/aarch64-sve-linux-sigcontext.h | 267 ------------------- gdbserver/Makefile.in | 2 +- gdbserver/configure.srv | 2 +- gdbserver/linux-aarch64-low.cc | 2 +- 12 files changed, 742 insertions(+), 737 deletions(-) create mode 100644 gdb/nat/aarch64-scalable-linux-ptrace.c create mode 100644 gdb/nat/aarch64-scalable-linux-ptrace.h create mode 100644 gdb/nat/aarch64-scalable-linux-sigcontext.h delete mode 100644 gdb/nat/aarch64-sve-linux-ptrace.c delete mode 100644 gdb/nat/aarch64-sve-linux-ptrace.h delete mode 100644 gdb/nat/aarch64-sve-linux-sigcontext.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9b992a3..cdfbad4 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1561,7 +1561,7 @@ HFILES_NO_SRCDIR = \ nat/aarch64-linux.h \ nat/aarch64-linux-hw-point.h \ nat/aarch64-mte-linux-ptrace.h \ - nat/aarch64-sve-linux-ptrace.h \ + nat/aarch64-scalable-linux-ptrace.h \ nat/amd64-linux-siginfo.h \ nat/gdb_ptrace.h \ nat/gdb_thread_db.h \ diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 8844fc7..9f32279 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -35,7 +35,7 @@ #include "arch/arm.h" #include "nat/aarch64-linux.h" #include "nat/aarch64-linux-hw-point.h" -#include "nat/aarch64-sve-linux-ptrace.h" +#include "nat/aarch64-scalable-linux-ptrace.h" #include "elf/external.h" #include "elf/common.h" diff --git a/gdb/configure.nat b/gdb/configure.nat index 2739d14..1dc4206 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -239,7 +239,7 @@ case ${gdb_host} in aarch32-linux-nat.o nat/aarch64-hw-point.o \ nat/aarch64-linux-hw-point.o \ nat/aarch64-linux.o \ - nat/aarch64-sve-linux-ptrace.o \ + nat/aarch64-scalable-linux-ptrace.o \ nat/aarch64-mte-linux-ptrace.o" ;; arc) diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c new file mode 100644 index 0000000..cc43f51 --- /dev/null +++ b/gdb/nat/aarch64-scalable-linux-ptrace.c @@ -0,0 +1,393 @@ +/* Common target dependent routines for AArch64 Scalable Extensions + (SVE/SME). + + Copyright (C) 2018-2023 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include "gdbsupport/common-defs.h" +#include "elf/external.h" +#include "elf/common.h" +#include "aarch64-scalable-linux-ptrace.h" +#include "arch/aarch64.h" +#include "gdbsupport/common-regcache.h" +#include "gdbsupport/byte-vector.h" +#include + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +uint64_t +aarch64_sve_get_vq (int tid) +{ + struct iovec iovec; + struct user_sve_header header; + + iovec.iov_len = sizeof (header); + iovec.iov_base = &header; + + /* Ptrace gives the vector length in bytes. Convert it to VQ, the number of + 128bit chunks in a Z register. We use VQ because 128bits is the minimum + a Z register can increase in size. */ + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + { + /* SVE is not supported. */ + return 0; + } + + uint64_t vq = sve_vq_from_vl (header.vl); + + if (!sve_vl_valid (header.vl)) + { + warning (_("Invalid SVE state from kernel; SVE disabled.")); + return 0; + } + + return vq; +} + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +bool +aarch64_sve_set_vq (int tid, uint64_t vq) +{ + struct iovec iovec; + struct user_sve_header header; + + iovec.iov_len = sizeof (header); + iovec.iov_base = &header; + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + { + /* SVE is not supported. */ + return false; + } + + header.vl = sve_vl_from_vq (vq); + + if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + { + /* Vector length change failed. */ + return false; + } + + return true; +} + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +bool +aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf) +{ + uint64_t reg_vg = 0; + + /* The VG register may not be valid if we've not collected any value yet. + This can happen, for example, if we're restoring the regcache after an + inferior function call, and the VG register comes after the Z + registers. */ + if (reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM) != REG_VALID) + { + /* If vg is not available yet, fetch it from ptrace. The VG value from + ptrace is likely the correct one. */ + uint64_t vq = aarch64_sve_get_vq (tid); + + /* If something went wrong, just bail out. */ + if (vq == 0) + return false; + + reg_vg = sve_vg_from_vq (vq); + } + else + reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg); + + return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg)); +} + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +std::unique_ptr +aarch64_sve_get_sveregs (int tid) +{ + struct iovec iovec; + uint64_t vq = aarch64_sve_get_vq (tid); + + if (vq == 0) + perror_with_name (_("Unable to fetch SVE register header")); + + /* A ptrace call with NT_ARM_SVE will return a header followed by either a + dump of all the SVE and FP registers, or an fpsimd structure (identical to + the one returned by NT_FPREGSET) if the kernel has not yet executed any + SVE code. Make sure we allocate enough space for a full SVE dump. */ + + iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + std::unique_ptr buf (new gdb_byte[iovec.iov_len]); + iovec.iov_base = buf.get (); + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + perror_with_name (_("Unable to fetch SVE registers")); + + return buf; +} + +/* If we are running in BE mode, byteswap the contents + of SRC to DST for SIZE bytes. Other, just copy the contents + from SRC to DST. */ + +static void +aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size) +{ + gdb_assert (src != nullptr && dst != nullptr); + gdb_assert (size > 1); + +#if (__BYTE_ORDER == __BIG_ENDIAN) + for (int i = 0; i < size - 1; i++) + dst[i] = src[size - i]; +#else + memcpy (dst, src, size); +#endif +} + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +void +aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, + const void *buf) +{ + char *base = (char *) buf; + struct user_sve_header *header = (struct user_sve_header *) buf; + + uint64_t vq = sve_vq_from_vl (header->vl); + uint64_t vg = sve_vg_from_vl (header->vl); + + /* Sanity check the data in the header. */ + if (!sve_vl_valid (header->vl) + || SVE_PT_SIZE (vq, header->flags) != header->size) + error (_("Invalid SVE header from kernel.")); + + /* Update VG. Note, the registers in the regcache will already be of the + correct length. */ + reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg); + + if (HAS_SVE_STATE (*header)) + { + /* The register dump contains a set of SVE registers. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + reg_buf->raw_supply (AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + reg_buf->raw_supply (AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + } + else + { + /* WARNING: SIMD state is laid out in memory in target-endian format, + while SVE state is laid out in an endianness-independent format (LE). + + So we have a couple cases to consider: + + 1 - If the target is big endian, then SIMD state is big endian, + requiring a byteswap. + + 2 - If the target is little endian, then SIMD state is little endian, + which matches the SVE format, so no byteswap is needed. */ + + /* There is no SVE state yet - the register dump contains a fpsimd + structure instead. These registers still exist in the hardware, but + the kernel has not yet initialised them, and so they will be null. */ + + gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + /* Make sure we have a zeroed register buffer. We will need the zero + padding below. */ + memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + /* Copy across the V registers from fpsimd structure to the Z registers, + ensuring the non overlapping state is set to null. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + { + /* Handle big endian/little endian SIMD/SVE conversion. */ + aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i], + V_REGISTER_SIZE); + reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg); + } + + reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + + /* Clear the SVE only registers. */ + memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg); + + reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg); + } +} + +/* See nat/aarch64-scalable-linux-ptrace.h. */ + +void +aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, + void *buf) +{ + struct user_sve_header *header = (struct user_sve_header *) buf; + char *base = (char *) buf; + uint64_t vq = sve_vq_from_vl (header->vl); + + /* Sanity check the data in the header. */ + if (!sve_vl_valid (header->vl) + || SVE_PT_SIZE (vq, header->flags) != header->size) + error (_("Invalid SVE header from kernel.")); + + if (!HAS_SVE_STATE (*header)) + { + /* There is no SVE state yet - the register dump contains a fpsimd + structure instead. Where possible we want to write the reg_buf data + back to the kernel using the fpsimd structure. However, if we cannot + then we'll need to reformat the fpsimd into a full SVE structure, + resulting in the initialization of SVE state written back to the + kernel, which is why we try to avoid it. */ + + bool has_sve_state = false; + gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + /* Check in the reg_buf if any of the Z registers are set after the + first 128 bits, or if any of the other SVE registers are set. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + { + has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, + reg, sizeof (__int128_t)); + if (has_sve_state) + break; + } + + if (!has_sve_state) + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + { + has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i, + reg, 0); + if (has_sve_state) + break; + } + + if (!has_sve_state) + has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM, + reg, 0); + + /* If no SVE state exists, then use the existing fpsimd structure to + write out state and return. */ + if (!has_sve_state) + { + /* WARNING: SIMD state is laid out in memory in target-endian format, + while SVE state is laid out in an endianness-independent format + (LE). + + So we have a couple cases to consider: + + 1 - If the target is big endian, then SIMD state is big endian, + requiring a byteswap. + + 2 - If the target is little endian, then SIMD state is little + endian, which matches the SVE format, so no byteswap is needed. */ + + /* The collects of the Z registers will overflow the size of a vreg. + There is enough space in the structure to allow for this, but we + cannot overflow into the next register as we might not be + collecting every register. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + { + if (REG_VALID + == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) + { + reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg); + /* Handle big endian/little endian SIMD/SVE conversion. */ + aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg, + V_REGISTER_SIZE); + } + } + + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + + return; + } + + /* Otherwise, reformat the fpsimd structure into a full SVE set, by + expanding the V registers (working backwards so we don't splat + registers before they are copied) and using null for everything else. + Note that enough space for a full SVE dump was originally allocated + for base. */ + + header->flags |= SVE_PT_REGS_SVE; + header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + + memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, + sizeof (uint32_t)); + memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, + sizeof (uint32_t)); + + for (int i = AARCH64_SVE_Z_REGS_NUM; i >= 0 ; i--) + { + memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i], + sizeof (__int128_t)); + } + } + + /* Replace the kernel values with those from reg_buf. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) + reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM + i)) + reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM)) + reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + +} diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.h b/gdb/nat/aarch64-scalable-linux-ptrace.h new file mode 100644 index 0000000..2847c4e --- /dev/null +++ b/gdb/nat/aarch64-scalable-linux-ptrace.h @@ -0,0 +1,73 @@ +/* Common target dependent definitions for AArch64 Scalable Extensions + (SVE/SME). + + Copyright (C) 2018-2023 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_AARCH64_SCALABLE_LINUX_PTRACE_H +#define NAT_AARCH64_SCALABLE_LINUX_PTRACE_H + +#include +#include + +/* The order in which and are included + can be important. often declares various PTRACE_* + enums. often defines preprocessor constants for + these very same symbols. When that's the case, build errors will + result when is included before . */ +#include +#include + +#ifndef SVE_SIG_ZREGS_SIZE +#include "aarch64-scalable-linux-sigcontext.h" +#endif + +/* Indicates whether a SVE ptrace header is followed by SVE registers or a + fpsimd structure. */ + +#define HAS_SVE_STATE(header) ((header).flags & SVE_PT_REGS_SVE) + +/* Read VQ for the given tid using ptrace. If SVE is not supported then zero + is returned (on a system that supports SVE, then VQ cannot be zero). */ + +uint64_t aarch64_sve_get_vq (int tid); + +/* Set VQ in the kernel for the given tid, using either the value VQ or + reading from the register VG in the register buffer. */ + +bool aarch64_sve_set_vq (int tid, uint64_t vq); +bool aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf); + +/* Read the current SVE register set using ptrace, allocating space as + required. */ + +extern std::unique_ptr aarch64_sve_get_sveregs (int tid); + +/* Put the registers from linux structure buf into register buffer. Assumes the + vector lengths in the register buffer match the size in the kernel. */ + +extern void aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, + const void *buf); + +/* Put the registers from register buffer into linux structure buf. Assumes the + vector lengths in the register buffer match the size in the kernel. */ + +extern void +aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, + void *buf); + +#endif /* NAT_AARCH64_SCALABLE_LINUX_PTRACE_H */ diff --git a/gdb/nat/aarch64-scalable-linux-sigcontext.h b/gdb/nat/aarch64-scalable-linux-sigcontext.h new file mode 100644 index 0000000..e0120e0 --- /dev/null +++ b/gdb/nat/aarch64-scalable-linux-sigcontext.h @@ -0,0 +1,270 @@ +/* Linux Kernel sigcontext definitions for AArch64 Scalable Extensions + (SVE/SME). + + Copyright (C) 2018-2023 Free Software Foundation, Inc. + Contributed by Arm Ltd. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef NAT_AARCH64_SCALABLE_LINUX_SIGCONTEXT_H +#define NAT_AARCH64_SCALABLE_LINUX_SIGCONTEXT_H + +#define SVE_MAGIC 0x53564501 + +struct sve_context { + struct _aarch64_ctx head; + __u16 vl; + __u16 __reserved[3]; +}; + +/* + * The SVE architecture leaves space for future expansion of the + * vector length beyond its initial architectural limit of 2048 bits + * (16 quadwords). + * + * See linux/Documentation/arm64/sve.txt for a description of the VL/VQ + * terminology. + */ +#define SVE_VQ_BYTES 16 /* number of bytes per quadword */ + +#define SVE_VQ_MIN 1 +#define SVE_VQ_MAX 512 + +#define SVE_VL_MIN (SVE_VQ_MIN * SVE_VQ_BYTES) +#define SVE_VL_MAX (SVE_VQ_MAX * SVE_VQ_BYTES) + +#define SVE_NUM_ZREGS 32 +#define SVE_NUM_PREGS 16 + +#define sve_vl_valid(vl) \ + ((vl) % SVE_VQ_BYTES == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX) + +/* + * If the SVE registers are currently live for the thread at signal delivery, + * sve_context.head.size >= + * SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve_context.vl)) + * and the register data may be accessed using the SVE_SIG_*() macros. + * + * If sve_context.head.size < + * SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve_context.vl)), + * the SVE registers were not live for the thread and no register data + * is included: in this case, the SVE_SIG_*() macros should not be + * used except for this check. + * + * The same convention applies when returning from a signal: a caller + * will need to remove or resize the sve_context block if it wants to + * make the SVE registers live when they were previously non-live or + * vice-versa. This may require the caller to allocate fresh + * memory and/or move other context blocks in the signal frame. + * + * Changing the vector length during signal return is not permitted: + * sve_context.vl must equal the thread's current vector length when + * doing a sigreturn. + * + * + * Note: for all these macros, the "vq" argument denotes the SVE + * vector length in quadwords (i.e., units of 128 bits). + * + * The correct way to obtain vq is to use sve_vq_from_vl(vl). The + * result is valid if and only if sve_vl_valid(vl) is true. This is + * guaranteed for a struct sve_context written by the kernel. + * + * + * Additional macros describe the contents and layout of the payload. + * For each, SVE_SIG_x_OFFSET(args) is the start offset relative to + * the start of struct sve_context, and SVE_SIG_x_SIZE(args) is the + * size in bytes: + * + * x type description + * - ---- ----------- + * REGS the entire SVE context + * + * ZREGS __uint128_t[SVE_NUM_ZREGS][vq] all Z-registers + * ZREG __uint128_t[vq] individual Z-register Zn + * + * PREGS uint16_t[SVE_NUM_PREGS][vq] all P-registers + * PREG uint16_t[vq] individual P-register Pn + * + * FFR uint16_t[vq] first-fault status register + * + * Additional data might be appended in the future. + */ + +#define SVE_SIG_ZREG_SIZE(vq) ((__u32)(vq) * SVE_VQ_BYTES) +#define SVE_SIG_PREG_SIZE(vq) ((__u32)(vq) * (SVE_VQ_BYTES / 8)) +#define SVE_SIG_FFR_SIZE(vq) SVE_SIG_PREG_SIZE(vq) + +#define SVE_SIG_REGS_OFFSET \ + ((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) + +#define SVE_SIG_ZREGS_OFFSET SVE_SIG_REGS_OFFSET +#define SVE_SIG_ZREG_OFFSET(vq, n) \ + (SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREG_SIZE(vq) * (n)) +#define SVE_SIG_ZREGS_SIZE(vq) \ + (SVE_SIG_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_SIG_ZREGS_OFFSET) + +#define SVE_SIG_PREGS_OFFSET(vq) \ + (SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREGS_SIZE(vq)) +#define SVE_SIG_PREG_OFFSET(vq, n) \ + (SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREG_SIZE(vq) * (n)) +#define SVE_SIG_PREGS_SIZE(vq) \ + (SVE_SIG_PREG_OFFSET(vq, SVE_NUM_PREGS) - SVE_SIG_PREGS_OFFSET(vq)) + +#define SVE_SIG_FFR_OFFSET(vq) \ + (SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREGS_SIZE(vq)) + +#define SVE_SIG_REGS_SIZE(vq) \ + (SVE_SIG_FFR_OFFSET(vq) + SVE_SIG_FFR_SIZE(vq) - SVE_SIG_REGS_OFFSET) + +#define SVE_SIG_CONTEXT_SIZE(vq) (SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq)) + +/* SVE/FP/SIMD state (NT_ARM_SVE) */ + +struct user_sve_header { + __u32 size; /* total meaningful regset content in bytes */ + __u32 max_size; /* maximum 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) + +#define SVE_PT_REGS_FPSIMD 0 +#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK + +/* + * Common SVE_PT_* flags: + * These must be kept in sync with prctl interface in + */ +#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 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 user_sve_header) + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) + +/* + * 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_OFFSET SVE_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: + * + * x type description + * - ---- ----------- + * ZREGS \ + * ZREG | + * PREGS | refer to + * PREG | + * FFR / + * + * FPSR uint32_t FPSR + * FPCR uint32_t FPCR + * + * 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_SIZE sizeof(__u32) +#define SVE_PT_SVE_FPCR_SIZE sizeof(__u32) + +#define __SVE_SIG_TO_PT(offset) \ + ((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET) + +#define SVE_PT_SVE_OFFSET SVE_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) + \ + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) +#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 + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) + +#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 /* NAT_AARCH64_SCALABLE_LINUX_SIGCONTEXT_H */ diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c deleted file mode 100644 index 5114653..0000000 --- a/gdb/nat/aarch64-sve-linux-ptrace.c +++ /dev/null @@ -1,392 +0,0 @@ -/* Common target dependent for AArch64 systems. - - Copyright (C) 2018-2023 Free Software Foundation, Inc. - - This file is part of GDB. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#include -#include -#include "gdbsupport/common-defs.h" -#include "elf/external.h" -#include "elf/common.h" -#include "aarch64-sve-linux-ptrace.h" -#include "arch/aarch64.h" -#include "gdbsupport/common-regcache.h" -#include "gdbsupport/byte-vector.h" -#include - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -uint64_t -aarch64_sve_get_vq (int tid) -{ - struct iovec iovec; - struct user_sve_header header; - - iovec.iov_len = sizeof (header); - iovec.iov_base = &header; - - /* Ptrace gives the vector length in bytes. Convert it to VQ, the number of - 128bit chunks in a Z register. We use VQ because 128bits is the minimum - a Z register can increase in size. */ - - if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) - { - /* SVE is not supported. */ - return 0; - } - - uint64_t vq = sve_vq_from_vl (header.vl); - - if (!sve_vl_valid (header.vl)) - { - warning (_("Invalid SVE state from kernel; SVE disabled.")); - return 0; - } - - return vq; -} - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -bool -aarch64_sve_set_vq (int tid, uint64_t vq) -{ - struct iovec iovec; - struct user_sve_header header; - - iovec.iov_len = sizeof (header); - iovec.iov_base = &header; - - if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) - { - /* SVE is not supported. */ - return false; - } - - header.vl = sve_vl_from_vq (vq); - - if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec) < 0) - { - /* Vector length change failed. */ - return false; - } - - return true; -} - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -bool -aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf) -{ - uint64_t reg_vg = 0; - - /* The VG register may not be valid if we've not collected any value yet. - This can happen, for example, if we're restoring the regcache after an - inferior function call, and the VG register comes after the Z - registers. */ - if (reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM) != REG_VALID) - { - /* If vg is not available yet, fetch it from ptrace. The VG value from - ptrace is likely the correct one. */ - uint64_t vq = aarch64_sve_get_vq (tid); - - /* If something went wrong, just bail out. */ - if (vq == 0) - return false; - - reg_vg = sve_vg_from_vq (vq); - } - else - reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg); - - return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg)); -} - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -std::unique_ptr -aarch64_sve_get_sveregs (int tid) -{ - struct iovec iovec; - uint64_t vq = aarch64_sve_get_vq (tid); - - if (vq == 0) - perror_with_name (_("Unable to fetch SVE register header")); - - /* A ptrace call with NT_ARM_SVE will return a header followed by either a - dump of all the SVE and FP registers, or an fpsimd structure (identical to - the one returned by NT_FPREGSET) if the kernel has not yet executed any - SVE code. Make sure we allocate enough space for a full SVE dump. */ - - iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); - std::unique_ptr buf (new gdb_byte[iovec.iov_len]); - iovec.iov_base = buf.get (); - - if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) - perror_with_name (_("Unable to fetch SVE registers")); - - return buf; -} - -/* If we are running in BE mode, byteswap the contents - of SRC to DST for SIZE bytes. Other, just copy the contents - from SRC to DST. */ - -static void -aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size) -{ - gdb_assert (src != nullptr && dst != nullptr); - gdb_assert (size > 1); - -#if (__BYTE_ORDER == __BIG_ENDIAN) - for (int i = 0; i < size - 1; i++) - dst[i] = src[size - i]; -#else - memcpy (dst, src, size); -#endif -} - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -void -aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, - const void *buf) -{ - char *base = (char *) buf; - struct user_sve_header *header = (struct user_sve_header *) buf; - - uint64_t vq = sve_vq_from_vl (header->vl); - uint64_t vg = sve_vg_from_vl (header->vl); - - /* Sanity check the data in the header. */ - if (!sve_vl_valid (header->vl) - || SVE_PT_SIZE (vq, header->flags) != header->size) - error (_("Invalid SVE header from kernel.")); - - /* Update VG. Note, the registers in the regcache will already be of the - correct length. */ - reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg); - - if (HAS_SVE_STATE (*header)) - { - /* The register dump contains a set of SVE registers. */ - - for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) - reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, - base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); - - for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) - reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, - base + SVE_PT_SVE_PREG_OFFSET (vq, i)); - - reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, - base + SVE_PT_SVE_FFR_OFFSET (vq)); - reg_buf->raw_supply (AARCH64_FPSR_REGNUM, - base + SVE_PT_SVE_FPSR_OFFSET (vq)); - reg_buf->raw_supply (AARCH64_FPCR_REGNUM, - base + SVE_PT_SVE_FPCR_OFFSET (vq)); - } - else - { - /* WARNING: SIMD state is laid out in memory in target-endian format, - while SVE state is laid out in an endianness-independent format (LE). - - So we have a couple cases to consider: - - 1 - If the target is big endian, then SIMD state is big endian, - requiring a byteswap. - - 2 - If the target is little endian, then SIMD state is little endian, - which matches the SVE format, so no byteswap is needed. */ - - /* There is no SVE state yet - the register dump contains a fpsimd - structure instead. These registers still exist in the hardware, but - the kernel has not yet initialised them, and so they will be null. */ - - gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); - struct user_fpsimd_state *fpsimd - = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); - - /* Make sure we have a zeroed register buffer. We will need the zero - padding below. */ - memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); - - /* Copy across the V registers from fpsimd structure to the Z registers, - ensuring the non overlapping state is set to null. */ - - for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) - { - /* Handle big endian/little endian SIMD/SVE conversion. */ - aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i], - V_REGISTER_SIZE); - reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg); - } - - reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); - reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); - - /* Clear the SVE only registers. */ - memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); - - for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) - reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg); - - reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg); - } -} - -/* See nat/aarch64-sve-linux-ptrace.h. */ - -void -aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, - void *buf) -{ - struct user_sve_header *header = (struct user_sve_header *) buf; - char *base = (char *) buf; - uint64_t vq = sve_vq_from_vl (header->vl); - - /* Sanity check the data in the header. */ - if (!sve_vl_valid (header->vl) - || SVE_PT_SIZE (vq, header->flags) != header->size) - error (_("Invalid SVE header from kernel.")); - - if (!HAS_SVE_STATE (*header)) - { - /* There is no SVE state yet - the register dump contains a fpsimd - structure instead. Where possible we want to write the reg_buf data - back to the kernel using the fpsimd structure. However, if we cannot - then we'll need to reformat the fpsimd into a full SVE structure, - resulting in the initialization of SVE state written back to the - kernel, which is why we try to avoid it. */ - - bool has_sve_state = false; - gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); - struct user_fpsimd_state *fpsimd - = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); - - memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); - - /* Check in the reg_buf if any of the Z registers are set after the - first 128 bits, or if any of the other SVE registers are set. */ - - for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) - { - has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, - reg, sizeof (__int128_t)); - if (has_sve_state) - break; - } - - if (!has_sve_state) - for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) - { - has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i, - reg, 0); - if (has_sve_state) - break; - } - - if (!has_sve_state) - has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM, - reg, 0); - - /* If no SVE state exists, then use the existing fpsimd structure to - write out state and return. */ - if (!has_sve_state) - { - /* WARNING: SIMD state is laid out in memory in target-endian format, - while SVE state is laid out in an endianness-independent format - (LE). - - So we have a couple cases to consider: - - 1 - If the target is big endian, then SIMD state is big endian, - requiring a byteswap. - - 2 - If the target is little endian, then SIMD state is little - endian, which matches the SVE format, so no byteswap is needed. */ - - /* The collects of the Z registers will overflow the size of a vreg. - There is enough space in the structure to allow for this, but we - cannot overflow into the next register as we might not be - collecting every register. */ - - for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) - { - if (REG_VALID - == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) - { - reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg); - /* Handle big endian/little endian SIMD/SVE conversion. */ - aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg, - V_REGISTER_SIZE); - } - } - - if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) - reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); - if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) - reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); - - return; - } - - /* Otherwise, reformat the fpsimd structure into a full SVE set, by - expanding the V registers (working backwards so we don't splat - registers before they are copied) and using null for everything else. - Note that enough space for a full SVE dump was originally allocated - for base. */ - - header->flags |= SVE_PT_REGS_SVE; - header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); - - memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, - sizeof (uint32_t)); - memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, - sizeof (uint32_t)); - - for (int i = AARCH64_SVE_Z_REGS_NUM; i >= 0 ; i--) - { - memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i], - sizeof (__int128_t)); - } - } - - /* Replace the kernel values with those from reg_buf. */ - - for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) - if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) - reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, - base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); - - for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) - if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM + i)) - reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i, - base + SVE_PT_SVE_PREG_OFFSET (vq, i)); - - if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM)) - reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM, - base + SVE_PT_SVE_FFR_OFFSET (vq)); - if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) - reg_buf->raw_collect (AARCH64_FPSR_REGNUM, - base + SVE_PT_SVE_FPSR_OFFSET (vq)); - if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) - reg_buf->raw_collect (AARCH64_FPCR_REGNUM, - base + SVE_PT_SVE_FPCR_OFFSET (vq)); - -} diff --git a/gdb/nat/aarch64-sve-linux-ptrace.h b/gdb/nat/aarch64-sve-linux-ptrace.h deleted file mode 100644 index 9539e19..0000000 --- a/gdb/nat/aarch64-sve-linux-ptrace.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Common target dependent for AArch64 systems. - - Copyright (C) 2018-2023 Free Software Foundation, Inc. - - This file is part of GDB. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef NAT_AARCH64_SVE_LINUX_PTRACE_H -#define NAT_AARCH64_SVE_LINUX_PTRACE_H - -#include -#include - -/* The order in which and are included - can be important. often declares various PTRACE_* - enums. often defines preprocessor constants for - these very same symbols. When that's the case, build errors will - result when is included before . */ -#include -#include - -#ifndef SVE_SIG_ZREGS_SIZE -#include "aarch64-sve-linux-sigcontext.h" -#endif - -/* Indicates whether a SVE ptrace header is followed by SVE registers or a - fpsimd structure. */ - -#define HAS_SVE_STATE(header) ((header).flags & SVE_PT_REGS_SVE) - -/* Read VQ for the given tid using ptrace. If SVE is not supported then zero - is returned (on a system that supports SVE, then VQ cannot be zero). */ - -uint64_t aarch64_sve_get_vq (int tid); - -/* Set VQ in the kernel for the given tid, using either the value VQ or - reading from the register VG in the register buffer. */ - -bool aarch64_sve_set_vq (int tid, uint64_t vq); -bool aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf); - -/* Read the current SVE register set using ptrace, allocating space as - required. */ - -extern std::unique_ptr aarch64_sve_get_sveregs (int tid); - -/* Put the registers from linux structure buf into register buffer. Assumes the - vector lengths in the register buffer match the size in the kernel. */ - -extern void aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, - const void *buf); - -/* Put the registers from register buffer into linux structure buf. Assumes the - vector lengths in the register buffer match the size in the kernel. */ - -extern void -aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, - void *buf); - -#endif /* NAT_AARCH64_SVE_LINUX_PTRACE_H */ diff --git a/gdb/nat/aarch64-sve-linux-sigcontext.h b/gdb/nat/aarch64-sve-linux-sigcontext.h deleted file mode 100644 index aba3c15..0000000 --- a/gdb/nat/aarch64-sve-linux-sigcontext.h +++ /dev/null @@ -1,267 +0,0 @@ -/* Copyright (C) 2018-2023 Free Software Foundation, Inc. - Contributed by Arm Ltd. - - This file is part of GDB. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef NAT_AARCH64_SVE_LINUX_SIGCONTEXT_H -#define NAT_AARCH64_SVE_LINUX_SIGCONTEXT_H - -#define SVE_MAGIC 0x53564501 - -struct sve_context { - struct _aarch64_ctx head; - __u16 vl; - __u16 __reserved[3]; -}; - -/* - * The SVE architecture leaves space for future expansion of the - * vector length beyond its initial architectural limit of 2048 bits - * (16 quadwords). - * - * See linux/Documentation/arm64/sve.txt for a description of the VL/VQ - * terminology. - */ -#define SVE_VQ_BYTES 16 /* number of bytes per quadword */ - -#define SVE_VQ_MIN 1 -#define SVE_VQ_MAX 512 - -#define SVE_VL_MIN (SVE_VQ_MIN * SVE_VQ_BYTES) -#define SVE_VL_MAX (SVE_VQ_MAX * SVE_VQ_BYTES) - -#define SVE_NUM_ZREGS 32 -#define SVE_NUM_PREGS 16 - -#define sve_vl_valid(vl) \ - ((vl) % SVE_VQ_BYTES == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX) - -/* - * If the SVE registers are currently live for the thread at signal delivery, - * sve_context.head.size >= - * SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve_context.vl)) - * and the register data may be accessed using the SVE_SIG_*() macros. - * - * If sve_context.head.size < - * SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve_context.vl)), - * the SVE registers were not live for the thread and no register data - * is included: in this case, the SVE_SIG_*() macros should not be - * used except for this check. - * - * The same convention applies when returning from a signal: a caller - * will need to remove or resize the sve_context block if it wants to - * make the SVE registers live when they were previously non-live or - * vice-versa. This may require the caller to allocate fresh - * memory and/or move other context blocks in the signal frame. - * - * Changing the vector length during signal return is not permitted: - * sve_context.vl must equal the thread's current vector length when - * doing a sigreturn. - * - * - * Note: for all these macros, the "vq" argument denotes the SVE - * vector length in quadwords (i.e., units of 128 bits). - * - * The correct way to obtain vq is to use sve_vq_from_vl(vl). The - * result is valid if and only if sve_vl_valid(vl) is true. This is - * guaranteed for a struct sve_context written by the kernel. - * - * - * Additional macros describe the contents and layout of the payload. - * For each, SVE_SIG_x_OFFSET(args) is the start offset relative to - * the start of struct sve_context, and SVE_SIG_x_SIZE(args) is the - * size in bytes: - * - * x type description - * - ---- ----------- - * REGS the entire SVE context - * - * ZREGS __uint128_t[SVE_NUM_ZREGS][vq] all Z-registers - * ZREG __uint128_t[vq] individual Z-register Zn - * - * PREGS uint16_t[SVE_NUM_PREGS][vq] all P-registers - * PREG uint16_t[vq] individual P-register Pn - * - * FFR uint16_t[vq] first-fault status register - * - * Additional data might be appended in the future. - */ - -#define SVE_SIG_ZREG_SIZE(vq) ((__u32)(vq) * SVE_VQ_BYTES) -#define SVE_SIG_PREG_SIZE(vq) ((__u32)(vq) * (SVE_VQ_BYTES / 8)) -#define SVE_SIG_FFR_SIZE(vq) SVE_SIG_PREG_SIZE(vq) - -#define SVE_SIG_REGS_OFFSET \ - ((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \ - / SVE_VQ_BYTES * SVE_VQ_BYTES) - -#define SVE_SIG_ZREGS_OFFSET SVE_SIG_REGS_OFFSET -#define SVE_SIG_ZREG_OFFSET(vq, n) \ - (SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREG_SIZE(vq) * (n)) -#define SVE_SIG_ZREGS_SIZE(vq) \ - (SVE_SIG_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_SIG_ZREGS_OFFSET) - -#define SVE_SIG_PREGS_OFFSET(vq) \ - (SVE_SIG_ZREGS_OFFSET + SVE_SIG_ZREGS_SIZE(vq)) -#define SVE_SIG_PREG_OFFSET(vq, n) \ - (SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREG_SIZE(vq) * (n)) -#define SVE_SIG_PREGS_SIZE(vq) \ - (SVE_SIG_PREG_OFFSET(vq, SVE_NUM_PREGS) - SVE_SIG_PREGS_OFFSET(vq)) - -#define SVE_SIG_FFR_OFFSET(vq) \ - (SVE_SIG_PREGS_OFFSET(vq) + SVE_SIG_PREGS_SIZE(vq)) - -#define SVE_SIG_REGS_SIZE(vq) \ - (SVE_SIG_FFR_OFFSET(vq) + SVE_SIG_FFR_SIZE(vq) - SVE_SIG_REGS_OFFSET) - -#define SVE_SIG_CONTEXT_SIZE(vq) (SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq)) - -/* SVE/FP/SIMD state (NT_ARM_SVE) */ - -struct user_sve_header { - __u32 size; /* total meaningful regset content in bytes */ - __u32 max_size; /* maximum 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) - -#define SVE_PT_REGS_FPSIMD 0 -#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK - -/* - * Common SVE_PT_* flags: - * These must be kept in sync with prctl interface in - */ -#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 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 user_sve_header) + (SVE_VQ_BYTES - 1)) \ - / SVE_VQ_BYTES * SVE_VQ_BYTES) - -/* - * 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_OFFSET SVE_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: - * - * x type description - * - ---- ----------- - * ZREGS \ - * ZREG | - * PREGS | refer to - * PREG | - * FFR / - * - * FPSR uint32_t FPSR - * FPCR uint32_t FPCR - * - * 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_SIZE sizeof(__u32) -#define SVE_PT_SVE_FPCR_SIZE sizeof(__u32) - -#define __SVE_SIG_TO_PT(offset) \ - ((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET) - -#define SVE_PT_SVE_OFFSET SVE_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) + \ - (SVE_VQ_BYTES - 1)) \ - / SVE_VQ_BYTES * SVE_VQ_BYTES) -#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 + (SVE_VQ_BYTES - 1)) \ - / SVE_VQ_BYTES * SVE_VQ_BYTES) - -#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 /* NAT_AARCH64_SVE_LINUX_SIGCONTEXT_H */ diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in index 39cb9e7..b597515 100644 --- a/gdbserver/Makefile.in +++ b/gdbserver/Makefile.in @@ -218,7 +218,7 @@ SFILES = \ $(srcdir)/../gdb/arch/ppc-linux-common.c \ $(srcdir)/../gdb/arch/riscv.c \ $(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \ - $(srcdir)/../gdb/nat/aarch64-sve-linux-ptrace.c \ + $(srcdir)/../gdb/nat/aarch64-scalable-linux-ptrace.c \ $(srcdir)/../gdb/nat/linux-btrace.c \ $(srcdir)/../gdb/nat/linux-namespaces.c \ $(srcdir)/../gdb/nat/linux-osdata.c \ diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index 72256f8..7a7575a 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -51,7 +51,7 @@ case "${gdbserver_host}" in srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o" srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o" srv_tgtobj="$srv_tgtobj nat/aarch64-mte-linux-ptrace.o" - srv_tgtobj="$srv_tgtobj nat/aarch64-sve-linux-ptrace.o" + srv_tgtobj="$srv_tgtobj nat/aarch64-scalable-linux-ptrace.o" srv_tgtobj="${srv_tgtobj} $srv_linux_obj" srv_linux_regsets=yes srv_linux_thread_db=yes diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index 2474a00..8b22f19 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -44,7 +44,7 @@ #include "linux-aarch32-tdesc.h" #include "linux-aarch64-tdesc.h" #include "nat/aarch64-mte-linux-ptrace.h" -#include "nat/aarch64-sve-linux-ptrace.h" +#include "nat/aarch64-scalable-linux-ptrace.h" #include "tdesc.h" #ifdef HAVE_SYS_REG_H -- cgit v1.1