aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Hayward <alan.hayward@arm.com>2018-05-11 11:52:56 +0100
committerSimon Marchi <simon.marchi@ericsson.com>2018-06-01 10:40:20 -0400
commite884af534b7e77ba214e8bd8d7ba8fb6009231a4 (patch)
tree9da28d4411ef18d194c40cad6130f8bdb0380399
parentd56bdeb9c2e434b91d5e6d605b92bb1cd21177e5 (diff)
Ptrace support for Aarch64 SVEusers/simark/regcache-for-alan
Add support for reading and writing registers for Aarch64 SVE. I've made this functionality common as it will be required for gdbserver when gdbsever sve support is added. Given that gdbserver does not yet call this function, I am happy to remove the regcache commonise functions from the previous patch. However, this would result in code in nat/ that does not compile for gdbserver. I wanted to avoid that. We need to support the cases where the kernel only gives us a fpsimd structure. This occurs when there is no active SVE state in the kernel (for example, after starting a new process). As per the covering email description, I've included large chunks of linux kernel headers within an ifdef. Formatting of these macros remains identical to the Kernel versions (ie not adapted to GNU style). Added checks to make sure the vector length has not changed whilst the process is running. 2018-05-11 Alan Hayward <alan.hayward@arm.com> gdb/ * aarch64-linux-nat.c (fetch_sveregs_from_thread): New function. (store_sveregs_to_thread): Likewise. (aarch64_linux_fetch_inferior_registers): Check for SVE. (aarch64_linux_store_inferior_registers): Likewise. * nat/aarch64-sve-linux-ptrace.c (aarch64_sve_get_sveregs): New function. (aarch64_sve_regs_copy_to_regcache): Likewise. (aarch64_sve_regs_copy_from_regcache): Likewise. * nat/aarch64-sve-linux-ptrace.h (aarch64_sve_get_sveregs): New declaration. (aarch64_sve_regs_copy_to_regcache): Likewise. (aarch64_sve_regs_copy_from_regcache): Likewise. (sve_context): Structure from Linux headers. (SVE_SIG_ZREGS_SIZE): Define from Linux headers. (SVE_SIG_ZREG_SIZE): Likewise. (SVE_SIG_PREG_SIZE): Likewise. (SVE_SIG_FFR_SIZE): Likewise. (SVE_SIG_REGS_OFFSET): Likewise. (SVE_SIG_ZREGS_OFFSET): Likewise. (SVE_SIG_ZREG_OFFSET): Likewise. (SVE_SIG_ZREGS_SIZE): Likewise. (SVE_SIG_PREGS_OFFSET): Likewise. (SVE_SIG_PREG_OFFSET): Likewise. (SVE_SIG_PREGS_SIZE): Likewise. (SVE_SIG_FFR_OFFSET): Likewise. (SVE_SIG_REGS_SIZE): Likewise. (SVE_SIG_CONTEXT_SIZE): Likewise. (SVE_PT_REGS_MASK): Likewise. (SVE_PT_REGS_FPSIMD): Likewise. (SVE_PT_REGS_SVE): Likewise. (SVE_PT_VL_INHERIT): Likewise. (SVE_PT_VL_ONEXEC): Likewise. (SVE_PT_REGS_OFFSET): Likewise. (SVE_PT_FPSIMD_OFFSET): Likewise. (SVE_PT_FPSIMD_SIZE): Likewise. (SVE_PT_SVE_ZREG_SIZE): Likewise. (SVE_PT_SVE_PREG_SIZE): Likewise. (SVE_PT_SVE_FFR_SIZE): Likewise. (SVE_PT_SVE_FPSR_SIZE): Likewise. (SVE_PT_SVE_FPCR_SIZE): Likewise. (__SVE_SIG_TO_PT): Likewise. (SVE_PT_SVE_OFFSET): Likewise. (SVE_PT_SVE_ZREGS_OFFSET): Likewise. (SVE_PT_SVE_ZREG_OFFSET): Likewise. (SVE_PT_SVE_ZREGS_SIZE): Likewise. (SVE_PT_SVE_PREGS_OFFSET): Likewise. (SVE_PT_SVE_PREG_OFFSET): Likewise. (SVE_PT_SVE_PREGS_SIZE): Likewise. (SVE_PT_SVE_FFR_OFFSET): Likewise. (SVE_PT_SVE_FPSR_OFFSET): Likewise. (SVE_PT_SVE_FPCR_OFFSET): Likewise. (SVE_PT_SVE_SIZE): Likewise. (SVE_PT_SIZE): Likewise. (HAS_SVE_STATE): New define. gdbserver * Makefile.in: Add aarch64-sve-linux-ptrace.c.
-rw-r--r--gdb/aarch64-linux-nat.c57
-rw-r--r--gdb/common/common-regcache.h9
-rw-r--r--gdb/gdbserver/Makefile.in1
-rw-r--r--gdb/gdbserver/regcache.c8
-rw-r--r--gdb/gdbserver/regcache.h20
-rw-r--r--gdb/nat/aarch64-sve-linux-ptrace.c258
-rw-r--r--gdb/nat/aarch64-sve-linux-ptrace.h122
-rw-r--r--gdb/regcache.c4
-rw-r--r--gdb/regcache.h10
9 files changed, 466 insertions, 23 deletions
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 79dd9ceca62..8165594272a 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -384,19 +384,65 @@ store_fpregs_to_thread (ptid_t ptid, const reg_buffer *regcache)
}
}
+/* Fill GDB's register array with the sve register values
+ from the current thread. */
+
+static void
+fetch_sveregs_from_thread (ptid_t ptid, reg_buffer *regcache)
+{
+ gdb_byte *base = aarch64_sve_get_sveregs (ptid.lwp ());
+ aarch64_sve_regs_copy_to_regcache (regcache, base);
+ xfree (base);
+}
+
+/* Store to the current thread the valid sve register
+ values in the GDB's register array. */
+
+static void
+store_sveregs_to_thread (ptid_t ptid, reg_buffer *regcache)
+{
+ gdb_byte *base;
+ int ret, tid;
+ struct iovec iovec;
+
+ tid = ptid_get_lwp (inferior_ptid);
+
+ /* Obtain a dump of SVE registers from ptrace. */
+ base = aarch64_sve_get_sveregs (tid);
+
+ /* Overwrite with regcache state. */
+ aarch64_sve_regs_copy_from_regcache (regcache, base);
+
+ /* Write back to the kernel. */
+ iovec.iov_base = base;
+ iovec.iov_len = ((struct user_sve_header *) base)->size;
+ ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec);
+ xfree (base);
+
+ if (ret < 0)
+ perror_with_name (_("Unable to store sve registers"));
+}
+
/* Implement the "fetch_registers" target_ops method. */
void
aarch64_linux_nat_target::fetch_registers (ptid_t ptid, reg_buffer *regcache,
int regno)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+
if (regno == -1)
{
fetch_gregs_from_thread (ptid, regcache);
- fetch_fpregs_from_thread (ptid, regcache);
+ if (tdep->has_sve ())
+ fetch_sveregs_from_thread (ptid, regcache);
+ else
+ fetch_fpregs_from_thread (ptid, regcache);
}
else if (regno < AARCH64_V0_REGNUM)
fetch_gregs_from_thread (ptid, regcache);
+ else if (tdep->has_sve ())
+ fetch_sveregs_from_thread (ptid, regcache);
else
fetch_fpregs_from_thread (ptid, regcache);
}
@@ -407,13 +453,20 @@ void
aarch64_linux_nat_target::store_registers (ptid_t ptid, reg_buffer *regcache,
int regno)
{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+
if (regno == -1)
{
store_gregs_to_thread (ptid, regcache);
- store_fpregs_to_thread (ptid, regcache);
+ if (tdep->has_sve ())
+ store_sveregs_to_thread (ptid, regcache);
+ else
+ store_fpregs_to_thread (ptid, regcache);
}
else if (regno < AARCH64_V0_REGNUM)
store_gregs_to_thread (ptid, regcache);
+ else if (tdep->has_sve ())
+ store_sveregs_to_thread (ptid, regcache);
else
store_fpregs_to_thread (ptid, regcache);
}
diff --git a/gdb/common/common-regcache.h b/gdb/common/common-regcache.h
index 9709ba414ee..3f18cceb222 100644
--- a/gdb/common/common-regcache.h
+++ b/gdb/common/common-regcache.h
@@ -62,4 +62,13 @@ extern enum register_status regcache_raw_read_unsigned
ULONGEST regcache_raw_get_unsigned (struct regcache *regcache, int regnum);
+struct reg_buffer_common
+{
+ virtual ~reg_buffer_common () = default;
+ virtual void raw_supply (int regnum, const void *buf) = 0;
+ virtual void raw_collect (int regnum, void *buf) const = 0;
+ virtual bool raw_compare (int regnum, const void *buf, int offset) const = 0;
+ virtual register_status get_register_status (int regnum) const = 0;
+};
+
#endif /* COMMON_REGCACHE_H */
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 675faa43642..f924e6a7f9e 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -219,6 +219,7 @@ SFILES = \
$(srcdir)/common/tdesc.c \
$(srcdir)/common/vec.c \
$(srcdir)/common/xml-utils.c \
+ $(srcdir)/nat/aarch64-sve-linux-ptrace.c \
$(srcdir)/nat/linux-btrace.c \
$(srcdir)/nat/linux-namespaces.c \
$(srcdir)/nat/linux-osdata.c \
diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c
index 88f0db04832..39bc6f095c6 100644
--- a/gdb/gdbserver/regcache.c
+++ b/gdb/gdbserver/regcache.c
@@ -159,7 +159,7 @@ init_register_cache (struct regcache *regcache,
struct regcache *
new_register_cache (const struct target_desc *tdesc)
{
- struct regcache *regcache = XCNEW (struct regcache);
+ struct regcache *regcache = new struct regcache;
gdb_assert (tdesc->registers_size != 0);
@@ -174,7 +174,7 @@ free_register_cache (struct regcache *regcache)
if (regcache->registers_owned)
free (regcache->registers);
free (regcache->register_status);
- free (regcache);
+ delete regcache;
}
}
@@ -506,10 +506,10 @@ regcache::get_register_status (int regnum) const
first OFFSET bytes) to the contents of BUF (without any offset). Returns 0
if identical. */
-int
+bool
regcache::raw_compare (int regnum, const void *buf, int offset) const
{
gdb_assert (register_size (tdesc, regnum) > offset);
return memcmp (buf, register_data (this, regnum, 1) + offset,
- register_size (tdesc, regnum) - offset);
+ register_size (tdesc, regnum) - offset) == 0;
}
diff --git a/gdb/gdbserver/regcache.h b/gdb/gdbserver/regcache.h
index b3631bebd27..ad199a9ef19 100644
--- a/gdb/gdbserver/regcache.h
+++ b/gdb/gdbserver/regcache.h
@@ -28,31 +28,31 @@ struct target_desc;
inferior; this is primarily for simplicity, as the performance
benefit is minimal. */
-struct regcache
+struct regcache : public reg_buffer_common
{
/* The regcache's target description. */
- const struct target_desc *tdesc;
+ const struct target_desc *tdesc = nullptr;
/* Whether the REGISTERS buffer's contents are valid. If false, we
haven't fetched the registers from the target yet. Not that this
register cache is _not_ pass-through, unlike GDB's. Note that
"valid" here is unrelated to whether the registers are available
in a traceframe. For that, check REGISTER_STATUS below. */
- int registers_valid;
- int registers_owned;
- unsigned char *registers;
+ int registers_valid = 0;
+ int registers_owned = 0;
+ unsigned char *registers = nullptr;
#ifndef IN_PROCESS_AGENT
/* One of REG_UNAVAILBLE or REG_VALID. */
- unsigned char *register_status;
+ unsigned char *register_status = nullptr;
#endif
- void raw_supply (int regnum, const void *buf);
+ void raw_supply (int regnum, const void *buf) override;
- void raw_collect (int regnum, void *buf) const;
+ void raw_collect (int regnum, void *buf) const override;
- int raw_compare (int regnum, const void *buf, int offset) const;
+ bool raw_compare (int regnum, const void *buf, int offset) const override;
- enum register_status get_register_status (int regnum) const;
+ enum register_status get_register_status (int regnum) const override;
};
struct regcache *init_register_cache (struct regcache *regcache,
diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c
index 3a1dbae7099..0f0b76c3198 100644
--- a/gdb/nat/aarch64-sve-linux-ptrace.c
+++ b/gdb/nat/aarch64-sve-linux-ptrace.c
@@ -25,6 +25,8 @@
#include "aarch64-sve-linux-ptrace.h"
#include "arch/aarch64.h"
+static bool vq_change_warned = false;
+
/* See nat/aarch64-sve-linux-ptrace.h. */
unsigned long
@@ -56,3 +58,259 @@ aarch64_sve_get_vq (int tid)
return vq;
}
+
+/* Read the current SVE register set using ptrace, allocating space as
+ required. */
+
+gdb_byte *
+aarch64_sve_get_sveregs (int tid)
+{
+ int ret;
+ struct iovec iovec;
+ struct user_sve_header header;
+ long 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);
+ iovec.iov_base = xmalloc (iovec.iov_len);
+
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec);
+ if (ret < 0)
+ perror_with_name (_("Unable to fetch sve registers"));
+
+ return (gdb_byte *) iovec.iov_base;
+}
+
+/* Put the registers from linux structure buf into regcache. */
+
+void
+aarch64_sve_regs_copy_to_regcache (reg_buffer_common *regcache, const void *buf)
+{
+ char *base = (char*) buf;
+ int i;
+ struct user_sve_header *header = (struct user_sve_header *) buf;
+ long vq, vg_regcache;
+
+ vq = sve_vq_from_vl (header->vl);
+
+ /* Sanity check the data in the header. */
+ gdb_assert (sve_vl_valid (header->vl));
+ gdb_assert (SVE_PT_SIZE (vq, header->flags) == header->size);
+
+ regcache->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_regcache);
+ if (vg_regcache == 0)
+ {
+ /* VG has not been set. */
+ vg_regcache = sve_vg_from_vl (header->vl);
+ regcache->raw_supply (AARCH64_SVE_VG_REGNUM, &vg_regcache);
+ }
+ else if (vg_regcache != sve_vg_from_vl (header->vl) && !vq_change_warned)
+ {
+ /* Vector length on the running process has changed. GDB currently does
+ not support this and will result in GDB showing incorrect partially
+ incorrect data for the vector registers. Warn once and continue. We
+ do not expect many programs to exhibit this behaviour. To fix this
+ we need to spot the change earlier and generate a new target
+ descriptor. */
+ warning (_("Vector length has changed (%ld to %d). "
+ "Vector registers may show incorrect data."),
+ vg_regcache, sve_vg_from_vl (header->vl));
+ vq_change_warned = true;
+ }
+
+ if (HAS_SVE_STATE (*header))
+ {
+ /* The register dump contains a set of SVE registers. */
+
+ for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
+ regcache->raw_supply (AARCH64_SVE_Z0_REGNUM + i,
+ base + SVE_PT_SVE_ZREG_OFFSET (vq, i));
+
+ for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
+ regcache->raw_supply (AARCH64_SVE_P0_REGNUM + i,
+ base + SVE_PT_SVE_PREG_OFFSET (vq, i));
+
+ regcache->raw_supply (AARCH64_SVE_FFR_REGNUM,
+ base + SVE_PT_SVE_FFR_OFFSET (vq));
+ regcache->raw_supply (AARCH64_FPSR_REGNUM,
+ base + SVE_PT_SVE_FPSR_OFFSET (vq));
+ regcache->raw_supply (AARCH64_FPCR_REGNUM,
+ base + SVE_PT_SVE_FPCR_OFFSET (vq));
+ }
+ else
+ {
+ /* 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. */
+
+ char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ struct user_fpsimd_state *fpsimd
+ = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
+
+ /* Copy across the V registers from fpsimd structure to the Z registers,
+ ensuring the non overlapping state is set to null. */
+
+ memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+
+ for (i = 0; i <= AARCH64_SVE_Z_REGS_NUM; i++)
+ {
+ memcpy (zero_reg, &fpsimd->vregs[i], sizeof (__int128_t));
+ regcache->raw_supply (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
+ }
+
+ regcache->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
+ regcache->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
+
+ /* Clear the SVE only registers. */
+
+ for (i = 0; i <= AARCH64_SVE_P_REGS_NUM; i++)
+ regcache->raw_supply (AARCH64_SVE_P0_REGNUM + i, zero_reg);
+
+ regcache->raw_supply (AARCH64_SVE_FFR_REGNUM, zero_reg);
+ }
+}
+
+
+/* Put the registers from regcache into linux structure buf. */
+
+void
+aarch64_sve_regs_copy_from_regcache (reg_buffer_common *regcache, void *buf)
+{
+ struct user_sve_header *header = (struct user_sve_header *) buf;
+ char *base = (char*) buf;
+ long vq, vg_regcache;
+ int i;
+
+ vq = sve_vq_from_vl (header->vl);
+
+ /* Sanity check the data in the header. */
+ gdb_assert (sve_vl_valid (header->vl));
+ gdb_assert (SVE_PT_SIZE (vq, header->flags) == header->size);
+
+ regcache->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_regcache);
+ if (vg_regcache != 0 && vg_regcache != sve_vg_from_vl (header->vl))
+ /* Vector length on the running process has changed. GDB currently does
+ not support this and will result in GDB writing invalid data back to the
+ vector registers. Error and exit. We do not expect many programs to
+ exhibit this behaviour. To fix this we need to spot the change earlier
+ and generate a new target descriptor. */
+ error (_("Vector length has changed. Cannot write back registers."));
+
+ 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 regcache 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. */
+
+ int has_sve_state = 0;
+ char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ struct user_fpsimd_state *fpsimd
+ = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
+
+ memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+
+ /* Check in the regcache if any of the Z registers are set after the
+ first 128 bits, or if any of the other SVE registers are set. */
+
+ for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
+ {
+ has_sve_state |= regcache->raw_compare (AARCH64_SVE_Z0_REGNUM + i,
+ zero_reg, sizeof (__int128_t));
+ if (has_sve_state != 0)
+ break;
+ }
+
+ if (has_sve_state == 0)
+ for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
+ {
+ has_sve_state |= regcache->raw_compare (AARCH64_SVE_P0_REGNUM + i,
+ zero_reg, 0);
+ if (has_sve_state != 0)
+ break;
+ }
+
+ if (has_sve_state == 0)
+ has_sve_state |= regcache->raw_compare (AARCH64_SVE_FFR_REGNUM,
+ zero_reg, 0);
+
+ /* If no SVE state exists, then use the existing fpsimd structure to
+ write out state and return. */
+
+ if (has_sve_state == 0)
+ {
+ /* 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 (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
+ {
+ if (REG_VALID == regcache->get_register_status (
+ AARCH64_SVE_Z0_REGNUM + i))
+ {
+ regcache->raw_collect (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
+ memcpy (&fpsimd->vregs[i], zero_reg, sizeof (__int128_t));
+ }
+ }
+
+ if (REG_VALID == regcache->get_register_status (AARCH64_FPSR_REGNUM))
+ regcache->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
+ if (REG_VALID == regcache->get_register_status (AARCH64_FPCR_REGNUM))
+ regcache->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 (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 regcache. */
+
+ for (i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
+ if (REG_VALID == regcache->get_register_status (AARCH64_SVE_Z0_REGNUM + i))
+ regcache->raw_collect (AARCH64_SVE_Z0_REGNUM + i,
+ base + SVE_PT_SVE_ZREG_OFFSET (vq, i));
+
+ for (i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
+ if (REG_VALID == regcache->get_register_status (AARCH64_SVE_P0_REGNUM + i))
+ regcache->raw_collect (AARCH64_SVE_P0_REGNUM + i,
+ base + SVE_PT_SVE_PREG_OFFSET (vq, i));
+
+ if (REG_VALID == regcache->get_register_status (AARCH64_SVE_FFR_REGNUM))
+ regcache->raw_collect (AARCH64_SVE_FFR_REGNUM,
+ base + SVE_PT_SVE_FFR_OFFSET (vq));
+ if (REG_VALID == regcache->get_register_status (AARCH64_FPSR_REGNUM))
+ regcache->raw_collect (AARCH64_FPSR_REGNUM,
+ base + SVE_PT_SVE_FPSR_OFFSET (vq));
+ if (REG_VALID == regcache->get_register_status (AARCH64_FPCR_REGNUM))
+ regcache->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
index a32ddf16768..1754989afd2 100644
--- a/gdb/nat/aarch64-sve-linux-ptrace.h
+++ b/gdb/nat/aarch64-sve-linux-ptrace.h
@@ -20,6 +20,8 @@
#ifndef AARCH64_SVE_LINUX_PTRACE_H
#define AARCH64_SVE_LINUX_PTRACE_H
+#include "common/common-regcache.h"
+
/* Where indicated, this file contains defines and macros lifted directly from
the Linux kernel headers, with no modification.
Refer to Linux kernel documentation for details. */
@@ -34,10 +36,31 @@
extern unsigned long aarch64_sve_get_vq (int tid);
+/* Read the current SVE register set using ptrace, allocating space as
+ required. */
+
+extern gdb_byte *aarch64_sve_get_sveregs (int tid);
+
+/* Put the registers from linux structure buf into regcache. */
+
+extern void aarch64_sve_regs_copy_to_regcache (reg_buffer_common *regcache,
+ const void *buf);
+
+/* Put the registers from regcache into linux structure buf. */
+
+extern void aarch64_sve_regs_copy_from_regcache (reg_buffer_common *regcache,
+ void *buf);
+
/* Structures and defines taken from sigcontext.h. */
#ifndef SVE_SIG_ZREGS_SIZE
+struct sve_context {
+ struct _aarch64_ctx head;
+ __u16 vl;
+ __u16 __reserved[3];
+};
+
#define SVE_VQ_BYTES 16 /* number of bytes per quadword */
#define SVE_VQ_MIN 1
@@ -52,6 +75,35 @@ extern unsigned long aarch64_sve_get_vq (int tid);
#define sve_vl_valid(vl) \
((vl) % SVE_VQ_BYTES == 0 && (vl) >= SVE_VL_MIN && (vl) <= SVE_VL_MAX)
+#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))
+
#endif /* SVE_SIG_ZREGS_SIZE. */
@@ -68,6 +120,76 @@ struct user_sve_header {
__u16 __reserved;
};
+
+#define SVE_PT_REGS_MASK 1
+
+#define SVE_PT_REGS_FPSIMD 0
+#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK
+
+#define SVE_PT_VL_INHERIT (PR_SVE_VL_INHERIT >> 16)
+#define SVE_PT_VL_ONEXEC (PR_SVE_SET_VL_ONEXEC >> 16)
+
+#define SVE_PT_REGS_OFFSET \
+ ((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \
+ / SVE_VQ_BYTES * SVE_VQ_BYTES)
+
+#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) + \
+ (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)
+
+#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 /* SVE_PT_SVE_ZREG_SIZE. */
+
+/* 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)
+
+
#endif /* aarch64-sve-linux-ptrace.h */
diff --git a/gdb/regcache.c b/gdb/regcache.c
index a2a43da0460..df521c94df7 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -1087,7 +1087,7 @@ reg_buffer::collect_regset (const struct regset *regset,
first OFFSET bytes) to the contents of BUF (without any offset). Returns 0
if identical. */
-int
+bool
reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
{
const char *regbuf;
@@ -1098,7 +1098,7 @@ reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
regbuf = (const char *) register_buffer (regnum);
size = m_descr->sizeof_register[regnum];
- return memcmp (buf, regbuf + offset, size - offset);
+ return memcmp (buf, regbuf + offset, size - offset) == 0;
}
/* Special handling for register PC. */
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 432e1b30a92..f271775b52a 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -121,7 +121,7 @@ typedef struct cached_reg
/* Buffer of registers. */
-class reg_buffer
+class reg_buffer : public reg_buffer_common
{
public:
reg_buffer (gdbarch *gdbarch, bool has_pseudo);
@@ -133,10 +133,10 @@ public:
/* Get the availability status of the value of register REGNUM in this
buffer. */
- enum register_status get_register_status (int regnum) const;
+ enum register_status get_register_status (int regnum) const override;
/* Collect register REGNUM from REGCACHE and store its contents in BUF. */
- void raw_collect (int regnum, void *buf) const;
+ void raw_collect (int regnum, void *buf) const override;
void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
bool is_signed) const;
@@ -145,7 +145,7 @@ public:
void *buf, size_t size) const;
/* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE. */
- void raw_supply (int regnum, const void *buf);
+ void raw_supply (int regnum, const void *buf) override;
void raw_supply (int regnum, const reg_buffer &src)
{
@@ -162,7 +162,7 @@ public:
void invalidate (int regnum);
- int raw_compare (int regnum, const void *buf, int offset) const;
+ bool raw_compare (int regnum, const void *buf, int offset) const override;
/* Dump the contents of a register from the register cache to the target
debug. */