]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Ptrace support for Aarch64 SVE users/simark/regcache-for-alan
authorAlan Hayward <alan.hayward@arm.com>
Fri, 11 May 2018 10:52:56 +0000 (11:52 +0100)
committerSimon Marchi <simon.marchi@ericsson.com>
Fri, 1 Jun 2018 14:40:20 +0000 (10:40 -0400)
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.

gdb/aarch64-linux-nat.c
gdb/common/common-regcache.h
gdb/gdbserver/Makefile.in
gdb/gdbserver/regcache.c
gdb/gdbserver/regcache.h
gdb/nat/aarch64-sve-linux-ptrace.c
gdb/nat/aarch64-sve-linux-ptrace.h
gdb/regcache.c
gdb/regcache.h

index 79dd9ceca625a4e4d70e26415c2c934fb46fccb1..8165594272a6545c6d03b7500c89237273a6e8b1 100644 (file)
@@ -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);
 }
index 9709ba414ee35b3b163015b331dbd8d21b2ecf06..3f18cceb2222244efe900ee6e3bc3585b1ec71fd 100644 (file)
@@ -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 */
index 675faa43642ffc716123e577dbe5cf806a5053bf..f924e6a7f9e53f584cc470c56643739c6f16e693 100644 (file)
@@ -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 \
index 88f0db048321304e653c4fc6842eb98c898955e9..39bc6f095c62400e7aed57e3281e77958c568a8d 100644 (file)
@@ -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;
 }
index b3631bebd27efc29196b5b819a0a2d2144e05a7f..ad199a9ef19db2e6fa99894b545bf63daaaa42f1 100644 (file)
@@ -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,
index 3a1dbae7099552907b71cb4efe8feb0e032dde49..0f0b76c319871a14d727d64eb7e4453530df86fa 100644 (file)
@@ -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));
+}
index a32ddf16768fa1985400ee05c37025b0be14b604..1754989afd2be190bf02401f0d822941e6433498 100644 (file)
@@ -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.  */
 
 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 */
index a2a43da04604a2a14c9c1c93f1628ded9bb8b8dd..df521c94df7051142d0d6cbd723bebb30c3f9cb2 100644 (file)
@@ -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.  */
index 432e1b30a92fa33728e1cdf17c4464502beb4508..f271775b52a4235d26adc1c4d3bf8258f5a4f6ae 100644 (file)
@@ -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.  */