[PATCH 6/8] AARCH64 SVE: Add SVE register support

Alan Hayward
2016-12-05T12:29:39Z

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 <signal.h>
#include <sys/user.h>
@@ -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, &regset->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, &regset->fpsr);
+ collect_register (regcache, AARCH64_FPCR_REGNO, &regset->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, &regset->fpsr);
+ supply_register (regcache, AARCH64_FPCR_REGNO, &regset->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 &regs_info_aarch64;
+ {
+ if (is_sve_tdesc ())
+ return &regs_info_aarch64_sve;
+ return &regs_info_aarch64;
+ }
else
return &regs_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. */