From patchwork Mon Dec 5 12:29:22 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alan Hayward X-Patchwork-Id: 18194 Received: (qmail 18238 invoked by alias); 5 Dec 2016 12:29:39 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 18224 invoked by uid 89); 5 Dec 2016 12:29:39 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.5 required=5.0 tests=BAYES_00, KAM_LOTSOFHASH, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 spammy=21407, Fields, fly, miscellaneous X-HELO: foss.arm.com Received: from foss.arm.com (HELO foss.arm.com) (217.140.101.70) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 05 Dec 2016 12:29:28 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 57BF815AB; Mon, 5 Dec 2016 04:29:27 -0800 (PST) Received: from [10.45.32.207] (e105284-mac.manchester.arm.com [10.45.32.207]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 65B083F445 for ; Mon, 5 Dec 2016 04:29:26 -0800 (PST) User-Agent: Microsoft-MacOutlook/14.7.0.161029 Date: Mon, 05 Dec 2016 12:29:22 +0000 Subject: [PATCH 6/8] AARCH64 SVE: Add SVE register support From: Alan Hayward To: Message-ID: Mime-version: 1.0 Content-type: text/plain; charset="UTF-8" Content-transfer-encoding: 7bit This is part of a series adding AARCH64 SVE support to gdb and gdbserver. This patch adds the SVE support for SVE registers. It includes: Register defines. DWARF register defines. Reading/Writing registers using ptrace. Pseudo V registers. Disabling tracepoints for SVE. Note that the Z registers occupy the same register ids in the register cache as the V registers. This ensures there is no gap in the register id numbers. There are an additional set of pseudo V register defines for SVE use. SVE support is determined using AARCH64_TDEP_HAS_SVE(), which currently always returns false. There is no code as of yet to initialise an AARCH64 SVE target description. Therefore all new code will remain unused until a later patch in the series. Ptrace code is a little tricker as we have two make two calls when doing a read. The first is to just get a header from which the full buffer size can be read and then used for the second call. We also have to deal with the case where there is not any SVE state in the kernel yet. When reading registers and there is no SVE kernel state we must expand the V registers into the SVE state. When writing registers and there is no SVE kernel state, we try to fit the SVE registers into the fpsimd state - however, if the higher Z register bits or the P registers are non null then we must write it as SVE state. Kernel support for SVE additions to Ptrace is currently under review here: http://archive.arm.linux.org.uk/lurker/message/20161125.193848.bec98fed.en. html The relevant header defines have been included in this patch (with a relevant ifdef guard) in order to build. Support for AARCH64 SVE tracepoints will be not be added in this set of patches, but will be added in a future patch. Tested on x86 and aarch64. Ok to commit? Alan. diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 65ca8ff2a7ff25b16e31135f4a8b11b6b013dae3..5525b0dd46a1bb641f4b6003942613437 202e785 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -49,6 +49,129 @@ #define TRAP_HWBKPT 0x0004 #endif +#define sve_vg_from_vl(vl) ((vl) / 8) +#define sve_vl_from_vg(vg) ((vg) * 8) + +/* Structures and defines taken from sigcontext.h. + These can be removed when kernel changes are fully committed. */ +#ifndef SVE_SIG_ZREGS_SIZE + +struct sve_context { + struct _aarch64_ctx head; + __u16 vl; + __u16 __reserved[3]; +}; + +#define SVE_VQ_MIN 1 +#define SVE_VQ_MAX 0x200 + +#define SVE_VL_MIN (SVE_VQ_MIN * 0x10) +#define SVE_VL_MAX (SVE_VQ_MAX * 0x10) + +#define SVE_NUM_ZREGS 32 +#define SVE_NUM_PREGS 16 + +#define sve_vq_from_vl(vl) ((vl) / 0x10) +#define sve_vl_valid(vl) \ + ((vl) % 0x10 == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX) + +#define SVE_SIG_ZREG_SIZE(vq) ((__u32)(vq) * 16) +#define SVE_SIG_PREG_SIZE(vq) ((__u32)(vq) * 2) +#define SVE_SIG_FFR_SIZE(vq) SVE_SIG_PREG_SIZE(vq) + +#define SVE_SIG_REGS_OFFSET ((sizeof (struct sve_context) + 15) / 16 * 16) + +#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)) + +#endif + +/* Structures and defines taken from ptrace.h. + These can be removed when kernel changes are fully committed. */ +#ifndef SVE_PT_SVE_ZREG_SIZE + +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; +}; + +#define SVE_PT_REGS_MASK (1 << 0) +#define SVE_PT_REGS_FPSIMD 0 +#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK + +#define SVE_PT_REGS_OFFSET ((sizeof (struct sve_context) + 15) / 16 * 16) + +#define SVE_PT_FPSIMD_OFFSET SVE_PT_REGS_OFFSET + +#define SVE_PT_FPSIMD_SIZE(vq, flags) (sizeof(struct user_fpsimd_state)) + +#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) + 15) / 16 * 16) +#define SVE_PT_SVE_FPCR_OFFSET(vq) \ + (SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE) + +#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 + /* Per-process data. We don't bind this to a per-inferior registry because of targets like x86 GNU/Linux that need to keep track of processes that aren't bound to any inferior (e.g., fork children, @@ -342,6 +465,266 @@ store_fpregs_to_thread (const struct regcache *regcache) } } +/* Fill GDB's register array with the sve register values + from the current thread. */ + +static void +fetch_sveregs_from_thread (struct regcache *regcache, int regno) +{ + int ret, tid, i; + struct iovec iovec; + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct user_sve_header header; + long vg, vq; + char *base; + + tid = ptid_get_lwp (inferior_ptid); + + /* First call ptrace to get a header. */ + + iovec.iov_len = sizeof (header); + iovec.iov_base = &header; + + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec); + if (ret < 0) + perror_with_name (_("Unable to fetch sve register header")); + + gdb_assert (sve_vl_valid (header.vl)); + vg = sve_vg_from_vl (header.vl); + vq = sve_vq_from_vl (header.vl); + gdb_assert (SVE_PT_SIZE (vq, header.flags) == header.size); + regcache_raw_supply (regcache, AARCH64_SVE_VG_REGNUM, &vg); + + /* Call again using the full buffer size. */ + + iovec.iov_len = header.size; + iovec.iov_base = xmalloc (iovec.iov_len); + base = (char*)iovec.iov_base; + + if (header.flags && SVE_PT_REGS_SVE) + { + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec); + if (ret < 0) + perror_with_name (_("Unable to fetch sve registers")); + + for (i = 0; i <= AARCH64_SVE_Z31_REGNUM - AARCH64_SVE_Z0_REGNUM; i++) + regcache_raw_supply (regcache, AARCH64_SVE_Z0_REGNUM + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (i = 0; i <= AARCH64_SVE_P15_REGNUM - AARCH64_SVE_P0_REGNUM; i++) + regcache_raw_supply (regcache, AARCH64_SVE_P0_REGNUM + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + regcache_raw_supply (regcache, AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + } + else + { + /* There is no SVE state. Read fpsimd instead. */ + char sve_reg[SVE_PT_SVE_ZREG_SIZE (vq)]; + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec); + if (ret < 0) + perror_with_name (_("Unable to fetch fpsimd registers via sve")); + + /* Clear the SVE only registers. */ + + memset (&sve_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + for (i = 0; i <= AARCH64_SVE_P15_REGNUM - AARCH64_SVE_P0_REGNUM; i++) + regcache_raw_supply (regcache, AARCH64_SVE_P0_REGNUM + i, &sve_reg); + + regcache_raw_supply (regcache, AARCH64_SVE_FFR_REGNUM, &sve_reg); + + /* Convert vregs from fpsimd state to zregs, ensuring additional state is + null. */ + + for (i = 0; i <= AARCH64_SVE_Z31_REGNUM - AARCH64_SVE_Z0_REGNUM; i++) + { + memcpy (&sve_reg, &fpsimd->vregs[i], sizeof (__int128)); + regcache_raw_supply (regcache, AARCH64_SVE_Z0_REGNUM + i, &sve_reg); + } + + regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + } + + free (iovec.iov_base); +} + +/* Store to the current thread the valid sve register + values in the GDB's register array. */ + +static void +store_sveregs_to_thread (const struct regcache *regcache, int regno) +{ + int ret, tid, r; + struct iovec iovec; + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct user_sve_header header; + long vq; + char *sve_reg_p, *base; + + tid = ptid_get_lwp (inferior_ptid); + + /* First call ptrace to get a header. */ + + iovec.iov_len = sizeof (header); + iovec.iov_base = &header; + + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec); + if (ret < 0) + perror_with_name (_("Unable to fetch sve register header")); + + gdb_assert (sve_vl_valid (header.vl)); + vq = sve_vq_from_vl (header.vl); + gdb_assert (SVE_PT_SIZE (vq, header.flags) == header.size); + + /* Get the full structure. */ + + if (header.flags && SVE_PT_REGS_SVE) + iovec.iov_len = header.size; + else + iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + iovec.iov_base = xmalloc (iovec.iov_len); + base = (char*)iovec.iov_base; + + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec); + if (ret < 0) + perror_with_name (_("Unable to fetch sve registers before storing")); + + if (!(header.flags && SVE_PT_REGS_SVE)) + { + /* There is no SVE state. Try to put the registers into an fpsimd + structure, whilst checking to see if there is any actual SVE + state. */ + int has_sve_state = 0; + char sve_reg[SVE_PT_SVE_ZREG_SIZE (vq)]; + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + memset (&sve_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + /* Check if any SVE state exists in the regcache. */ + + for (r = AARCH64_SVE_Z0_REGNUM; r <= AARCH64_SVE_Z31_REGNUM; r++) + { + has_sve_state |= regcache_raw_compare (regcache, r, &sve_reg, + sizeof (__int128)); + if (has_sve_state != 0) + break; + } + + if (has_sve_state == 0) + for (r = AARCH64_SVE_P0_REGNUM; r <= AARCH64_SVE_P15_REGNUM; r++) + { + has_sve_state |= regcache_raw_compare (regcache, r, &sve_reg, 0); + if (has_sve_state != 0) + break; + } + + if (has_sve_state == 0) + has_sve_state |= regcache_raw_compare (regcache, + AARCH64_SVE_FFR_REGNUM, + &sve_reg, 0); + + /* If no SVE state then copy into a fpsimd structure. */ + + if (has_sve_state == 0) + { + for (r = AARCH64_SVE_Z0_REGNUM; r <= AARCH64_SVE_Z31_REGNUM; r++) + { + if (REG_VALID == regcache_register_status (regcache, r)) + regcache_raw_collect ( + regcache, r, &fpsimd->vregs[r - AARCH64_SVE_Z0_REGNUM]); + } + + regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + + ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec); + + xfree (iovec.iov_base); + + if (ret < 0) + perror_with_name (_("Unable to store sve registers using fpsimd")); + + return; + } + + /* Expand the current kernel state into an SVE set (with null values for + the P and FFR regs) and set that as the current iov_base. */ + + int sve_size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + char *sve_buf = (char*)xmalloc (sve_size); + struct user_sve_header *sve_header = (struct user_sve_header *)sve_buf; + + memcpy (sve_buf, iovec.iov_base, sizeof (struct user_sve_header)); + sve_header->size = sve_size; + sve_header->flags |= SVE_PT_REGS_SVE; + + for (r = 0; r <= AARCH64_SVE_Z31_REGNUM - AARCH64_SVE_Z0_REGNUM; r++) + { + memcpy (sve_buf + SVE_PT_SVE_ZREG_OFFSET (vq, r), + &fpsimd->vregs[r], sizeof (__int128)); + } + + memcpy (sve_buf + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, + sizeof (uint32_t)); + memcpy (sve_buf + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, + sizeof (uint32_t)); + + xfree (iovec.iov_base); + iovec.iov_base = sve_buf; + iovec.iov_len = sve_size; + base = (char*)iovec.iov_base; + } + + /* Replace the kernel values with those from regcache. */ + + sve_reg_p = base + SVE_PT_SVE_ZREGS_OFFSET; + for (r = AARCH64_SVE_Z0_REGNUM; r <= AARCH64_SVE_Z31_REGNUM; r++) + { + if (REG_VALID == regcache_register_status (regcache, r)) + regcache_raw_collect (regcache, r, sve_reg_p); + sve_reg_p += SVE_PT_SVE_ZREG_SIZE (vq); + } + + sve_reg_p = base + SVE_PT_SVE_PREGS_OFFSET (vq); + for (r = AARCH64_SVE_P0_REGNUM; r <= AARCH64_SVE_P15_REGNUM; r++) + { + if (REG_VALID == regcache_register_status (regcache, r)) + regcache_raw_collect (regcache, r, sve_reg_p); + sve_reg_p += SVE_PT_SVE_PREG_SIZE (vq); + } + + if (REG_VALID == regcache_register_status (regcache, AARCH64_SVE_FFR_REGNUM)) + regcache_raw_collect (regcache, AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + if (REG_VALID == regcache_register_status (regcache, AARCH64_FPSR_REGNUM)) + regcache_raw_collect (regcache, AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + if (REG_VALID == regcache_register_status (regcache, AARCH64_FPCR_REGNUM)) + regcache_raw_collect (regcache, AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + + /* Note: We do not set VL. Changing VL on the fly is currently unsupported + in Linux. */ + + ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec); + + xfree (iovec.iov_base); + + if (ret < 0) + perror_with_name (_("Unable to store sve registers")); +} + /* Implement the "to_fetch_register" target_ops method. */ static void @@ -349,13 +732,21 @@ aarch64_linux_fetch_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) { + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + if (regno == -1) { fetch_gregs_from_thread (regcache); - fetch_fpregs_from_thread (regcache); + if (AARCH64_TDEP_HAS_SVE (tdep)) + fetch_sveregs_from_thread (regcache, regno); + else + fetch_fpregs_from_thread (regcache); } else if (regno < AARCH64_V0_REGNUM) fetch_gregs_from_thread (regcache); + else if (AARCH64_TDEP_HAS_SVE (tdep)) + fetch_sveregs_from_thread (regcache, regno); else fetch_fpregs_from_thread (regcache); } @@ -367,13 +758,21 @@ aarch64_linux_store_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) { + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + if (regno == -1) { store_gregs_to_thread (regcache); - store_fpregs_to_thread (regcache); + if (AARCH64_TDEP_HAS_SVE (tdep)) + store_sveregs_to_thread (regcache, regno); + else + store_fpregs_to_thread (regcache); } else if (regno < AARCH64_V0_REGNUM) store_gregs_to_thread (regcache); + else if (AARCH64_TDEP_HAS_SVE (tdep)) + store_sveregs_to_thread (regcache, regno); else store_fpregs_to_thread (regcache); } diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 625282070480017cd1e960ae3bd62c94f9163aa3..2eaf268e9ea2e1a7912c5778033d3ff01 737a648 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -30,6 +30,10 @@ struct regset; #define AARCH64_DWARF_X0 0 #define AARCH64_DWARF_SP 31 #define AARCH64_DWARF_V0 64 +#define AARCH64_DWARF_SVE_VG 46 +#define AARCH64_DWARF_SVE_FFR 47 +#define AARCH64_DWARF_SVE_P0 48 +#define AARCH64_DWARF_SVE_Z0 96 /* Register numbers of various important registers. */ enum aarch64_regnum @@ -46,8 +50,17 @@ enum aarch64_regnum /* Last floating point / vector register */ AARCH64_V31_REGNUM = AARCH64_V0_REGNUM + 31, - AARCH64_FPSR_REGNUM, /* Floating point status register */ - AARCH64_FPCR_REGNUM, /* Floating point control register */ + + AARCH64_SVE_Z0_REGNUM = AARCH64_V0_REGNUM, /* First SVE register (Reuses + the V register set). */ + AARCH64_SVE_Z31_REGNUM = AARCH64_V31_REGNUM, /* Last SVE register. */ + AARCH64_FPSR_REGNUM, /* Floating point status register. */ + AARCH64_FPCR_REGNUM, /* Floating point control register. */ + AARCH64_SVE_P0_REGNUM, /* First SVE predicate register. */ + AARCH64_SVE_P15_REGNUM = AARCH64_SVE_P0_REGNUM + 15, /* Last SVE predicate + register. */ + AARCH64_SVE_FFR_REGNUM, /* SVE First Failure register. */ + AARCH64_SVE_VG_REGNUM, /* SVE Vector Granual. */ /* Other useful registers. */ @@ -94,11 +107,14 @@ struct gdbarch_tdep struct type *vns_type; struct type *vnh_type; struct type *vnb_type; + struct type *vnv_type; /* syscall record. */ int (*aarch64_syscall_record) (struct regcache *regcache, unsigned long svc_number); }; +#define AARCH64_TDEP_HAS_SVE(tdep) (0) + extern struct target_desc *tdesc_aarch64; extern int aarch64_process_record (struct gdbarch *gdbarch, diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index aba7a8525529102d01e3c7cd282a9ee793d643fe..9808997664597484d006d380e00384152 8512ca5 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -66,6 +66,7 @@ #define AARCH64_S0_REGNUM (AARCH64_D0_REGNUM + 32) #define AARCH64_H0_REGNUM (AARCH64_S0_REGNUM + 32) #define AARCH64_B0_REGNUM (AARCH64_H0_REGNUM + 32) +#define AARCH64_SVE_V0_REGNUM (AARCH64_B0_REGNUM + 32) /* The standard register names, and all the valid aliases for them. */ static const struct @@ -150,6 +151,27 @@ static const char *const aarch64_v_register_names[] = "fpcr" }; +/* The SVE 'Z' and 'P' registers. */ +static const char *const aarch64_sve_register_names[] = +{ + /* These registers must appear in consecutive RAW register number + order and they must begin with AARCH64_SVE_Z0_REGNUM! */ + "z0", "z1", "z2", "z3", + "z4", "z5", "z6", "z7", + "z8", "z9", "z10", "z11", + "z12", "z13", "z14", "z15", + "z16", "z17", "z18", "z19", + "z20", "z21", "z22", "z23", + "z24", "z25", "z26", "z27", + "z28", "z29", "z30", "z31", + "fpsr", "fpcr", + "p0", "p1", "p2", "p3", + "p4", "p5", "p6", "p7", + "p8", "p9", "p10", "p11", + "p12", "p13", "p14", "p15", + "ffr", "vg" +}; + /* AArch64 prologue cache structure. */ struct aarch64_prologue_cache { @@ -1554,11 +1576,40 @@ aarch64_vnb_type (struct gdbarch *gdbarch) return tdep->vnb_type; } +/* Return the type for an AdvSISD V register. */ + +static struct type * +aarch64_vnv_type (struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep->vnv_type == NULL) + { + struct type *t; + struct type *elem; + + t = arch_composite_type (gdbarch, "__gdb_builtin_type_vnv", + TYPE_CODE_UNION); + + append_composite_type_field (t, "d", aarch64_vnd_type (gdbarch)); + append_composite_type_field (t, "s", aarch64_vns_type (gdbarch)); + append_composite_type_field (t, "h", aarch64_vnh_type (gdbarch)); + append_composite_type_field (t, "b", aarch64_vnb_type (gdbarch)); + append_composite_type_field (t, "q", aarch64_vnq_type (gdbarch)); + + tdep->vnv_type = t; + } + + return tdep->vnv_type; +} + /* Implement the "dwarf2_reg_to_regnum" gdbarch method. */ static int aarch64_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + if (reg >= AARCH64_DWARF_X0 && reg <= AARCH64_DWARF_X0 + 30) return AARCH64_X0_REGNUM + reg - AARCH64_DWARF_X0; @@ -1568,6 +1619,21 @@ aarch64_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) if (reg >= AARCH64_DWARF_V0 && reg <= AARCH64_DWARF_V0 + 31) return AARCH64_V0_REGNUM + reg - AARCH64_DWARF_V0; + if (AARCH64_TDEP_HAS_SVE (tdep)) + { + if (reg == AARCH64_DWARF_SVE_VG) + return AARCH64_SVE_VG_REGNUM; + + if (reg == AARCH64_DWARF_SVE_FFR) + return AARCH64_SVE_FFR_REGNUM; + + if (reg >= AARCH64_DWARF_SVE_P0 && reg <= AARCH64_DWARF_SVE_P0 + 15) + return AARCH64_SVE_P0_REGNUM + reg - AARCH64_DWARF_SVE_P0; + + if (reg >= AARCH64_DWARF_SVE_Z0 && reg <= AARCH64_DWARF_SVE_Z0 + 15) + return AARCH64_SVE_Z0_REGNUM + reg - AARCH64_DWARF_SVE_Z0; + } + return -1; } @@ -1902,6 +1968,8 @@ aarch64_gen_return_address (struct gdbarch *gdbarch, static const char * aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + static const char *const q_name[] = { "q0", "q1", "q2", "q3", @@ -1979,6 +2047,25 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return b_name[regnum - AARCH64_B0_REGNUM]; + if (AARCH64_TDEP_HAS_SVE (tdep)) + { + static const char *const sve_v_name[] = + { + "v0", "v1", "v2", "v3", + "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", + "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", + "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", + "v28", "v29", "v30", "v31", + }; + + if (regnum >= AARCH64_SVE_V0_REGNUM + && regnum < AARCH64_SVE_V0_REGNUM + 32) + return sve_v_name[regnum - AARCH64_SVE_V0_REGNUM]; + } + internal_error (__FILE__, __LINE__, _("aarch64_pseudo_register_name: bad register number %d"), regnum); @@ -1989,6 +2076,8 @@ aarch64_pseudo_register_name (struct gdbarch *gdbarch, int regnum) static struct type * aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + regnum -= gdbarch_num_regs (gdbarch); if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32) @@ -2006,6 +2095,10 @@ aarch64_pseudo_register_type (struct gdbarch *gdbarch, int regnum) if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return aarch64_vnb_type (gdbarch); + if (AARCH64_TDEP_HAS_SVE (tdep) && regnum >= AARCH64_SVE_V0_REGNUM + && regnum < AARCH64_SVE_V0_REGNUM + 32) + return aarch64_vnv_type (gdbarch); + internal_error (__FILE__, __LINE__, _("aarch64_pseudo_register_type: bad register number %d"), regnum); @@ -2017,6 +2110,8 @@ static int aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum, struct reggroup *group) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + regnum -= gdbarch_num_regs (gdbarch); if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32) @@ -2031,6 +2126,9 @@ aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum, return group == all_reggroup || group == vector_reggroup; else if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32) return group == all_reggroup || group == vector_reggroup; + else if (AARCH64_TDEP_HAS_SVE (tdep) && regnum >= AARCH64_SVE_V0_REGNUM + && regnum < AARCH64_SVE_V0_REGNUM + 32) + return group == all_reggroup || group == vector_reggroup; return group == all_reggroup; } @@ -2042,6 +2140,7 @@ aarch64_pseudo_read_value (struct gdbarch *gdbarch, struct regcache *regcache, int regnum) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); gdb_byte reg_buf[MAX_REGISTER_SIZE]; struct value *result_value; gdb_byte *buf; @@ -2128,6 +2227,22 @@ aarch64_pseudo_read_value (struct gdbarch *gdbarch, return result_value; } + if (AARCH64_TDEP_HAS_SVE (tdep) && regnum >= AARCH64_SVE_V0_REGNUM + && regnum < AARCH64_SVE_V0_REGNUM + 32) + { + enum register_status status; + unsigned v_regnum; + + v_regnum = AARCH64_V0_REGNUM + regnum - AARCH64_SVE_V0_REGNUM; + status = regcache_raw_read (regcache, v_regnum, reg_buf); + if (status != REG_VALID) + mark_value_bytes_unavailable (result_value, 0, + TYPE_LENGTH (value_type (result_value))); + else + memcpy (buf, reg_buf, V_REGISTER_SIZE); + return result_value; + } + gdb_assert_not_reached ("regnum out of bound"); } @@ -2137,6 +2252,7 @@ static void aarch64_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache, int regnum, const gdb_byte *buf) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); gdb_byte reg_buf[MAX_REGISTER_SIZE]; /* Ensure the register buffer is zero, we want gdb writes of the @@ -2201,6 +2317,18 @@ aarch64_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache, return; } + if (AARCH64_TDEP_HAS_SVE (tdep) && regnum >= AARCH64_SVE_V0_REGNUM + && regnum < AARCH64_SVE_V0_REGNUM + 32) + { + /* pseudo V registers. */ + unsigned v_regnum; + + v_regnum = AARCH64_V0_REGNUM + regnum - AARCH64_SVE_V0_REGNUM; + memcpy (reg_buf, buf, V_REGISTER_SIZE); + regcache_raw_write (regcache, v_regnum, reg_buf); + return; + } + gdb_assert_not_reached ("regnum out of bound"); } diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c index a4cf4746285683e1529142b0073f58b68eed9cf3..6a6fc5b22dabd2d3000300aa5212f4331 25ab751 100644 --- a/gdb/gdbserver/linux-aarch64-low.c +++ b/gdb/gdbserver/linux-aarch64-low.c @@ -28,6 +28,8 @@ #include "elf/common.h" #include "ax.h" #include "tracepoint.h" +#include "tdesc.h" +#include "regdef.h" #include #include @@ -39,6 +41,129 @@ #include "gdb_proc_service.h" +#define sve_vg_from_vl(vl) ((vl) / 8) +#define sve_vl_from_vg(vg) ((vg) * 8) + +/* Structures and defines taken from sigcontext.h. + These can be removed when kernel changes are fully committed. */ +#ifndef SVE_SIG_ZREGS_SIZE + +struct sve_context { + struct _aarch64_ctx head; + __u16 vl; + __u16 __reserved[3]; +}; + +#define SVE_VQ_MIN 1 +#define SVE_VQ_MAX 0x200 + +#define SVE_VL_MIN (SVE_VQ_MIN * 0x10) +#define SVE_VL_MAX (SVE_VQ_MAX * 0x10) + +#define SVE_NUM_ZREGS 32 +#define SVE_NUM_PREGS 16 + +#define sve_vq_from_vl(vl) ((vl) / 0x10) +#define sve_vl_valid(vl) \ + ((vl) % 0x10 == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX) + +#define SVE_SIG_ZREG_SIZE(vq) ((__u32)(vq) * 16) +#define SVE_SIG_PREG_SIZE(vq) ((__u32)(vq) * 2) +#define SVE_SIG_FFR_SIZE(vq) SVE_SIG_PREG_SIZE(vq) + +#define SVE_SIG_REGS_OFFSET ((sizeof (struct sve_context) + 15) / 16 * 16) + +#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)) + +#endif + +/* Structures and defines taken from ptrace.h. + These can be removed when kernel changes are fully committed. */ +#ifndef SVE_PT_SVE_ZREG_SIZE + +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; +}; + +#define SVE_PT_REGS_MASK (1 << 0) +#define SVE_PT_REGS_FPSIMD 0 +#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK + +#define SVE_PT_REGS_OFFSET ((sizeof (struct sve_context) + 15) / 16 * 16) + +#define SVE_PT_FPSIMD_OFFSET SVE_PT_REGS_OFFSET + +#define SVE_PT_FPSIMD_SIZE(vq, flags) (sizeof(struct user_fpsimd_state)) + +#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) + 15) / 16 * 16) +#define SVE_PT_SVE_FPCR_OFFSET(vq) \ + (SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE) + +#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 + /* Defined in auto-generated files. */ void init_registers_aarch64 (void); extern const struct target_desc *tdesc_aarch64; @@ -49,15 +174,22 @@ extern const struct target_desc *tdesc_aarch64; #define AARCH64_X_REGS_NUM 31 #define AARCH64_V_REGS_NUM 32 +#define AARCH64_SVE_Z_REGS_NUM AARCH64_V_REGS_NUM +#define AARCH64_SVE_P_REGS_NUM 16 #define AARCH64_X0_REGNO 0 #define AARCH64_SP_REGNO 31 #define AARCH64_PC_REGNO 32 #define AARCH64_CPSR_REGNO 33 #define AARCH64_V0_REGNO 34 +#define AARCH64_SVE_Z0_REGNO AARCH64_V0_REGNO #define AARCH64_FPSR_REGNO (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM) #define AARCH64_FPCR_REGNO (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM + 1) +#define AARCH64_SVE_P0_REGNO (AARCH64_FPCR_REGNO + 1) +#define AARCH64_SVE_FFR_REGNO (AARCH64_SVE_P0_REGNO + AARCH64_SVE_P_REGS_NUM) +#define AARCH64_SVE_VG_REGNO (AARCH64_SVE_FFR_REGNO + 1) -#define AARCH64_NUM_REGS (AARCH64_V0_REGNO + AARCH64_V_REGS_NUM + 2) +#define AARCH64_NUM_REGS AARCH64_FPCR_REGNO + 1 +#define AARCH64_SVE_NUM_REGS AARCH64_SVE_VG_REGNO + 1 /* Per-process arch-specific data we want to keep. */ @@ -86,6 +218,16 @@ is_64bit_tdesc (void) return register_size (regcache->tdesc, 0) == 8; } +/* Return true if the Z and P registers exist. */ + +static int +is_sve_tdesc (void) +{ + struct regcache *regcache = get_thread_regcache (current_thread, 0); + + return regcache->tdesc->num_registers == AARCH64_SVE_NUM_REGS; +} + /* Implementation of linux_target_ops method "cannot_store_register". */ static int @@ -153,6 +295,201 @@ aarch64_store_fpregset (struct regcache *regcache, const void *buf) supply_register (regcache, AARCH64_FPCR_REGNO, ®set->fpcr); } +static void +aarch64_fill_svefpregset (struct regcache *regcache, void *buf) +{ + struct user_fpsimd_state *regset = (struct user_fpsimd_state *) buf; + + collect_register (regcache, AARCH64_FPSR_REGNO, ®set->fpsr); + collect_register (regcache, AARCH64_FPCR_REGNO, ®set->fpcr); +} + +static void +aarch64_store_svefpregset (struct regcache *regcache, const void *buf) +{ + const struct user_fpsimd_state *regset + = (const struct user_fpsimd_state *) buf; + + supply_register (regcache, AARCH64_FPSR_REGNO, ®set->fpsr); + supply_register (regcache, AARCH64_FPCR_REGNO, ®set->fpcr); +} + +/* Put the registers from regcache into linux structure buf. */ + +static void +aarch64_fill_sveregset (struct regcache *regcache, void *buf) +{ + struct user_sve_header *header = (struct user_sve_header *) buf; + char *base = (char*) buf; + long vq; + int i; + + /* Note: We do not set VL. Changing VL on the fly is currently unsupported + in Linux. */ + gdb_assert (sve_vl_valid (header->vl)); + vq = sve_vq_from_vl (header->vl); + gdb_assert (SVE_PT_SIZE (vq, header->flags) == header->size); + + if (!(header->flags && SVE_PT_REGS_SVE)) + { + /* There is no SVE state, however our buffer does have enough room to fit + a full SVE state. Try to put the registers into an fpsimd structure, + whilst checking to see if there is any actual SVE state. */ + + int has_sve_state = 0; + char sve_reg[SVE_PT_SVE_ZREG_SIZE (vq)]; + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + memset (&sve_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + /* Check if any SVE state exists in the regcache. */ + + for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + { + has_sve_state |= compare_register (regcache, + AARCH64_SVE_Z0_REGNO + i, &sve_reg, + sizeof (__int128)); + if (has_sve_state != 0) + break; + } + + if (has_sve_state == 0) + for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + { + has_sve_state |= compare_register (regcache, + AARCH64_SVE_P0_REGNO + i, + &sve_reg, 0); + if (has_sve_state != 0) + break; + } + + if (has_sve_state == 0) + has_sve_state |= compare_register (regcache, AARCH64_SVE_FFR_REGNO, + &sve_reg, 0); + + /* If no SVE state then copy into a fpsimd structure. */ + + if (has_sve_state == 0) + { + /* These collects will overflow the size of a vreg, but that does not + matter as we have enough room in the buffer and we know that the + overflow will always be 0s. */ + for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + collect_register (regcache, AARCH64_SVE_Z0_REGNO + i, + &fpsimd->vregs[i]); + + collect_register (regcache, AARCH64_FPSR_REGNO, &fpsimd->fpsr); + collect_register (regcache, AARCH64_FPCR_REGNO, &fpsimd->fpcr); + + return; + } + + /* Otherwise, enable SVE state in the header. */ + + header->flags |= SVE_PT_REGS_SVE; + header->size = SVE_PT_SIZE (vq, header->flags); + memset (base + SVE_PT_SVE_OFFSET, 0, + SVE_PT_SVE_SIZE (vq, SVE_PT_REGS_SVE)); + } + + for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + collect_register (regcache, AARCH64_SVE_Z0_REGNO + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + collect_register (regcache, AARCH64_SVE_P0_REGNO + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + collect_register (regcache, AARCH64_SVE_FFR_REGNO, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + collect_register (regcache, AARCH64_FPSR_REGNO, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + collect_register (regcache, AARCH64_FPCR_REGNO, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); +} + +/* Put the registers from linux structure buf into regcache. */ + +static void +aarch64_store_sveregset (struct regcache *regcache, const void *buf) +{ + struct user_sve_header *header = (struct user_sve_header *) buf; + char *base = (char*) buf; + long vg, vq; + int i; + + gdb_assert (sve_vl_valid (header->vl)); + vg = sve_vg_from_vl (header->vl); + vq = sve_vq_from_vl (header->vl); + gdb_assert (SVE_PT_SIZE (vq, header->flags) == header->size); + supply_register (regcache, AARCH64_SVE_VG_REGNO, &vg); + + if (header->flags && SVE_PT_REGS_SVE) + { + for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + supply_register (regcache, AARCH64_SVE_Z0_REGNO + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + supply_register (regcache, AARCH64_SVE_P0_REGNO + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + supply_register (regcache, AARCH64_SVE_FFR_REGNO, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + supply_register (regcache, AARCH64_FPSR_REGNO, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + supply_register (regcache, AARCH64_FPCR_REGNO, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + } + else + { + /* There is no SVE state. */ + char sve_reg[SVE_PT_SVE_ZREG_SIZE (vq)]; + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + /* Clear the SVE only registers. */ + + memset (&sve_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); + + for (i = 0; i <= AARCH64_SVE_P_REGS_NUM; i++) + supply_register (regcache, AARCH64_SVE_P0_REGNO + i, &sve_reg); + + supply_register (regcache, AARCH64_SVE_FFR_REGNO, &sve_reg); + + /* Convert vregs from fpsimd state to zregs, ensuring additional state is + null. */ + + for (i = 0; i <= AARCH64_SVE_Z_REGS_NUM; i++) + { + memcpy (&sve_reg, &fpsimd->vregs[i], sizeof (__int128)); + supply_register (regcache, AARCH64_SVE_Z0_REGNO + i, &sve_reg); + } + + supply_register (regcache, AARCH64_FPSR_REGNO, &fpsimd->fpsr); + supply_register (regcache, AARCH64_FPCR_REGNO, &fpsimd->fpcr); + } +} + +/* Given an sve header from ptrace, return the size of a full register set + (with header). */ + +static int +aarch64_get_full_sve_size (const void *buf) +{ + struct user_sve_header *header = (struct user_sve_header *) buf; + + gdb_assert (sve_vl_valid (header->vl)); + + if (header->flags && SVE_PT_REGS_SVE) + return header->size; + else + /* There is no SVE state. If we are writing state back to the kernel then + there needs to be enough room to fit a current sized SVE state. */ + return SVE_PT_SIZE (sve_vq_from_vl (header->vl), SVE_PT_REGS_SVE); +} + /* Enable miscellaneous debugging output. The name is historical - it was originally used to debug LinuxThreads support. */ extern int debug_threads; @@ -526,13 +863,48 @@ static struct regs_info regs_info_aarch64 = &aarch64_regsets_info, }; +static struct regset_info aarch64_sve_regsets[] = +{ + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, + sizeof (struct user_pt_regs), GENERAL_REGS, + aarch64_fill_gregset, aarch64_store_gregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct user_fpsimd_state), FP_REGS, + aarch64_fill_svefpregset, aarch64_store_svefpregset + }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_SVE, + sizeof (struct user_sve_header), EXTENDED_REGS, + aarch64_fill_sveregset, aarch64_store_sveregset, + aarch64_get_full_sve_size + }, + NULL_REGSET +}; + +static struct regsets_info aarch64_sve_regsets_info = + { + aarch64_sve_regsets, /* regsets. */ + 0, /* num_regsets. */ + NULL, /* disabled_regsets. */ + }; + +static struct regs_info regs_info_aarch64_sve = + { + NULL, /* regset_bitmap. */ + NULL, /* usrregs. */ + &aarch64_sve_regsets_info, + }; + /* Implementation of linux_target_ops method "regs_info". */ static const struct regs_info * aarch64_regs_info (void) { if (is_64bit_tdesc ()) - return ®s_info_aarch64; + { + if (is_sve_tdesc ()) + return ®s_info_aarch64_sve; + return ®s_info_aarch64; + } else return ®s_info_aarch32; } @@ -546,8 +918,8 @@ aarch64_supports_tracepoints (void) return 1; else { - /* We don't support tracepoints on aarch32 now. */ - return is_64bit_tdesc (); + /* We don't support tracepoints on either aarch32 or SVE. */ + return is_64bit_tdesc () && !is_sve_tdesc (); } } @@ -3022,4 +3394,5 @@ initialize_low_arch (void) initialize_low_arch_aarch32 (); initialize_regsets_info (&aarch64_regsets_info); + initialize_regsets_info (&aarch64_sve_regsets_info); } diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index b5ac8de5ccee13866d6b74a34d3db87412537e66..78a671fe13f689c64b64378eeafe2ded3 e309ca7 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -32,6 +32,7 @@ #ifdef HAVE_LINUX_REGSETS typedef void (*regset_fill_func) (struct regcache *, void *); typedef void (*regset_store_func) (struct regcache *, const void *); +typedef int (*regset_get_full_size_func) (const void *); enum regset_type { GENERAL_REGS, FP_REGS, @@ -53,6 +54,7 @@ struct regset_info enum regset_type type; regset_fill_func fill_function; regset_store_func store_function; + regset_get_full_size_func get_full_size_function; }; /* Aggregation of all the supported regsets of a given diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 602f7376c60a9b7b23496e4e0dea437e985f7c71..595144f7123c72a248dda651bd7c42b04 0f286a7 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -5490,6 +5490,38 @@ regsets_fetch_inferior_registers (struct regsets_info *regsets_info, #else res = ptrace (regset->get_request, pid, data, nt_type); #endif + + if (res == 0 && regset->get_full_size_function != NULL) + { + /* First call only obtained a partial set of data. Use obtained data + to get the full size and then call ptrace again. */ + + int full_size = regset->get_full_size_function (buf); + + if (full_size <= 0) + res = -1; + else + { + free (buf); + buf = xmalloc (full_size); + + if (nt_type) + { + iov.iov_base = buf; + iov.iov_len = full_size; + } + else + data = buf; + +#ifndef __sparc__ + res = ptrace (regset->get_request, pid, + (PTRACE_TYPE_ARG3) (long) nt_type, data); +#else + res = ptrace (regset->get_request, pid, data, nt_type); +#endif + } + } + if (res < 0) { if (errno == EIO) @@ -5574,6 +5606,37 @@ regsets_store_inferior_registers (struct regsets_info *regsets_info, res = ptrace (regset->get_request, pid, data, nt_type); #endif + if (res == 0 && regset->get_full_size_function != NULL) + { + /* First call only obtained a partial set of data. Use obtained data + to get the full size and then call ptrace again. */ + + int full_size = regset->get_full_size_function (buf); + + if (full_size <= 0) + res = -1; + else + { + free (buf); + buf = xmalloc (full_size); + + if (nt_type) + { + iov.iov_base = buf; + iov.iov_len = full_size; + } + else + data = buf; + +#ifndef __sparc__ + res = ptrace (regset->get_request, pid, + (PTRACE_TYPE_ARG3) (long) nt_type, data); +#else + res = ptrace (regset->get_request, pid, data, nt_type); +#endif + } + } + if (res == 0) { /* Then overlay our cached registers on that. */ diff --git a/include/elf/common.h b/include/elf/common.h index da79613e6dbad54003e921c2fbe2b30b34c21595..80a4d730b5c86e8cfdb29e1163e934b9f 8a7b498 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -605,6 +605,7 @@ /* note name must be "LINUX". */ #define NT_ARM_HW_WATCH 0x403 /* AArch hardware watchpoint registers */ /* note name must be "LINUX". */ +#define NT_ARM_SVE 0x405 /* ARM SVE registers. */ #define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */ #define NT_FILE 0x46494c45 /* Description of mapped files. */