diff mbox

[7/8] AARCH64 SVE: Enable AArch64 SVE in gdb

Message ID D46B0E4A.154FF%alan.hayward@arm.com
State New
Headers show

Commit Message

Alan Hayward Dec. 5, 2016, 12:30 p.m. UTC
This is part of a series adding SVE support to gdb and gdbserver.

This patch enables SVE support for the AArch64 version of gdb.

On AArch64 the size of the V and P registers are dependant on the value of
VG
which is read and set using the ptrace command.
The aarch64 version of the to_thread_architecture target_ops method ensures
that the current VG value is read before finding the target descriptor.

The initialize_tdesc_aarch64_sve () function (added in a previous patch)
will
initialise a target descriptor with an implied VG value of 1.

Upon encountering a new value of VG, the target will intialise a new
AArch64
SVE target descriptor, and then scale all the variable types and registers
by
the VG value. This will be stored in an array of all possible value of VG.


Tested on x86 and AArch64.
Ok to commit?

Alan.


 {
@@ -1304,6 +1304,31 @@ tdesc_create_reg (struct tdesc_feature *feature,
const char *name,
   VEC_safe_push (tdesc_reg_p, feature->registers, reg);
 }

+/* For a given feature, rescale the vector types and all vector
registers.  */
+void
+tdesc_rescale_vector_types (struct tdesc_feature *feature, int scale)
+{
+  int ix;
+  struct tdesc_type *type;
+
+  for (ix = 0; VEC_iterate (tdesc_type_p, feature->types, ix, type); ix++)
+    if (type->kind == TDESC_TYPE_VECTOR)
+      type->u.v.count *= scale;
+}
+
+/* For a given feature, rescale the registers with the given type name.
*/
+void
+tdesc_rescale_registers (struct tdesc_feature *feature, char *type_name,
+			 int scale)
+{
+  int ix;
+  struct tdesc_reg *reg;
+
+  for (ix = 0; VEC_iterate (tdesc_reg_p, feature->registers, ix, reg);
ix++)
+    if (reg->tdesc_type && 0 == strcmp (reg->tdesc_type->name, type_name))
+      reg->bitsize *= scale;
+}
+
 /* Subroutine of tdesc_free_feature to simplify it.
    Note: We do not want to free any referenced types here (e.g., types of
    fields of a struct).  All types of a feature are recorded in
diff mbox

Patch

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 
5525b0dd46a1bb641f4b6003942613437202e785..97212864dcce84779b3a86355f0ac69c5
0e3b20e 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -44,6 +44,7 @@ 

 /* Defines ps_err_e, struct ps_prochandle.  */
 #include "gdb_proc_service.h"
+#include "arch-utils.h"

 #ifndef TRAP_HWBKPT
 #define TRAP_HWBKPT 0x0004
@@ -191,6 +192,34 @@  struct aarch64_process_info

 static struct aarch64_process_info *aarch64_process_list = NULL;

+
+/* Read VG for the given tid using ptrace.  If SVE is not supported then
zero
+   is returned.  */
+
+static unsigned long
+aarch64_read_vg (int tid)
+{
+  int ret;
+  struct iovec iovec;
+  struct user_sve_header header;
+
+  iovec.iov_len = sizeof (header);
+  iovec.iov_base = &header;
+
+  /* Ptrace gives the vector length in bytes.  Convert it to VG - which
is the
+     number of 64bit chunks.  We use VG because that is the value that
appears
+     as a DWARF register.  */
+
+  ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec);
+  if (ret >= 0)
+    {
+      gdb_assert (sve_vl_valid (header.vl));
+      return sve_vg_from_vl (header.vl);
+    }
+  /* SVE is not supported.  */
+  return 0;
+}
+
 /* Find process data for process PID.  */

 static struct aarch64_process_info *
@@ -900,7 +929,7 @@  aarch64_linux_read_description (struct target_ops *ops)
   if (ret == 0)
     return tdesc_arm_with_neon;
   else
-    return tdesc_aarch64;
+    return aarch64_get_tdesc (aarch64_read_vg (tid));
 }

 /* Convert a native/host siginfo object, into/from the siginfo in the
@@ -1179,6 +1208,20 @@  aarch64_linux_can_do_single_step (struct target_ops
*target)
   return 1;
 }

+/* Implement the "to_thread_architecture" target_ops method.  */
+
+static struct gdbarch *
+aarch64_thread_architecture (struct target_ops *ops, ptid_t ptid)
+{
+  long vg = aarch64_read_vg (ptid_get_lwp (ptid));
+  struct gdbarch_info info;
+
+  gdbarch_info_init (&info);
+  info.bfd_arch_info = bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu);
+  info.tdep_info = &vg;
+  return gdbarch_find_by_info (info);
+}
+
 /* Define AArch64 maintenance commands.  */

 static void
@@ -1231,6 +1274,7 @@  _initialize_aarch64_linux_nat (void)
   t->to_watchpoint_addr_within_range =
     aarch64_linux_watchpoint_addr_within_range;
   t->to_can_do_single_step = aarch64_linux_can_do_single_step;
+  t->to_thread_architecture = aarch64_thread_architecture;

   /* Override the GNU/Linux inferior startup hook.  */
   super_post_startup_inferior = t->to_post_startup_inferior;
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index 
2eaf268e9ea2e1a7912c5778033d3ff01737a648..2c789e7aca1c3c4e3150c88d53aae5afb
41e2410 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -111,11 +111,14 @@  struct gdbarch_tdep

   /* syscall record.  */
   int (*aarch64_syscall_record) (struct regcache *regcache, unsigned long
svc_number);
+
+  /* For SVE targets, the VG value.  */
+  long vg;
 };

-#define AARCH64_TDEP_HAS_SVE(tdep) (0)
+#define AARCH64_TDEP_HAS_SVE(tdep) (tdep->vg != 0)

-extern struct target_desc *tdesc_aarch64;
+struct target_desc *aarch64_get_tdesc (long vg);

 extern int aarch64_process_record (struct gdbarch *gdbarch,
                                struct regcache *regcache, CORE_ADDR addr);
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 
9808997664597484d006d380e003841528512ca5..e1cdee9e35534e1910c31feca5208ff1c
d5bc8b5 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -51,6 +51,7 @@ 
 #include "record.h"
 #include "record-full.h"
 #include "features/aarch64.c"
+#include "features/aarch64-sve.c"
 #include "arch/aarch64-insn.h"
 #include "opcode/aarch64.h"
 #include <algorithm>
@@ -68,6 +69,12 @@ 
 #define AARCH64_B0_REGNUM (AARCH64_H0_REGNUM + 32)
 #define AARCH64_SVE_V0_REGNUM (AARCH64_B0_REGNUM + 32)

+/* Maximum supported VG value.  Increase if required.  */
+#define AARCH64_TDEP_MAX_SVE_VG  32
+
+/* All possible aarch64 target descriptors.  */
+struct target_desc *tdesc_aarch64_sve_list[AARCH64_TDEP_MAX_SVE_VG + 1];
+
 /* The standard register names, and all the valid aliases for them.  */
 static const struct
 {
@@ -2783,6 +2790,42 @@  aarch64_target_get_tdep_info (void *registers)
   return NULL;
 }

+/* Get the correct target description for the given VG value.
+   If VG is zero then it is assumed SVE is not supported.
+   (It is not possible to set VG to zero on an SVE system).  */
+struct target_desc *
+aarch64_get_tdesc (long vg)
+{
+  if (vg > AARCH64_TDEP_MAX_SVE_VG)
+    error (_("VG is %ld, maximum supported value is %d"), vg,
+	   AARCH64_TDEP_MAX_SVE_VG);
+  struct target_desc *tdesc;
+
+  if (vg == 0)
+    return tdesc_aarch64;
+
+  tdesc = tdesc_aarch64_sve_list[vg];
+
+  if (tdesc == NULL)
+    {
+      struct tdesc_feature *feature;
+
+      /* The target descriptor for the given VG value has not been
accessed
+	 yet.  Allocate a new one and then update all the register sizes.  */
+
+      initialize_tdesc_aarch64_sve ();
+      tdesc = tdesc_aarch64_sve;
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");
+      tdesc_rescale_vector_types (feature, vg);
+      tdesc_rescale_registers (feature, "svev", vg);
+      tdesc_rescale_registers (feature, "svep", vg);
+
+      tdesc_aarch64_sve_list[vg] = tdesc;
+    }
+  return tdesc;
+}
+
 /* Initialize the current architecture based on INFO.  If possible,
    re-use an architecture from ARCHES, which is a list of
    architectures already created during this debugging session.
@@ -2800,19 +2843,35 @@  aarch64_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
   const struct target_desc *tdesc = info.target_desc;
   int i;
   int valid_p = 1;
-  const struct tdesc_feature *feature;
+  const struct tdesc_feature *feature_core;
+  const struct tdesc_feature *feature_fpu;
+  const struct tdesc_feature *feature_sve;
   int num_regs = 0;
   int num_pseudo_regs = 0;
-
-  /* Ensure we always have a target descriptor.  */
-  if (!tdesc_has_registers (tdesc))
-    tdesc = tdesc_aarch64;
-
+  long vg = 0;
+
+  if (info.tdep_info)
+    vg = *(long *)info.tdep_info;
+  else if (tdesc && tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve"))
+    /* The current target descriptor says we have SVE but we were not
given a
+       VG value.  Set VG to the minimum SVE supported value.  */
+    vg = 2;
+
+  if (vg > AARCH64_TDEP_MAX_SVE_VG)
+    internal_error (__FILE__, __LINE__, _("VG out of bounds: %ld (max
%d)"),
+		    vg, AARCH64_TDEP_MAX_SVE_VG);
+
+  /* Ensure we always have a target descriptor, and that it is for the
current
+     VG value.  */
+  if (!tdesc_has_registers (tdesc) || vg > 0)
+    tdesc = aarch64_get_tdesc (vg);
   gdb_assert (tdesc);

-  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.core");
+  feature_core = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.core");
+  feature_fpu = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpu");
+  feature_sve = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");

-  if (feature == NULL)
+  if (feature_core == NULL)
     return NULL;

   tdesc_data = tdesc_data_alloc ();
@@ -2820,25 +2879,47 @@  aarch64_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
   /* Validate the descriptor provides the mandatory core R registers
      and allocate their numbers.  */
   for (i = 0; i < ARRAY_SIZE (aarch64_r_register_names); i++)
-    valid_p &=
-      tdesc_numbered_register (feature, tdesc_data, AARCH64_X0_REGNUM + i,
-			       aarch64_r_register_names[i]);
+    valid_p &= tdesc_numbered_register (feature_core, tdesc_data,
+					AARCH64_X0_REGNUM + i,
+					aarch64_r_register_names[i]);

   num_regs = AARCH64_X0_REGNUM + i;

-  /* Look for the V registers.  */
-  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpu");
-  if (feature)
+  /* Add the V registers.  */
+  if (feature_fpu != NULL)
     {
+      gdb_assert (feature_sve == NULL);
+      gdb_assert (vg == 0);
+
       /* Validate the descriptor provides the mandatory V registers
-         and allocate their numbers.  */
+	 and allocate their numbers.  */
       for (i = 0; i < ARRAY_SIZE (aarch64_v_register_names); i++)
-	valid_p &=
-	  tdesc_numbered_register (feature, tdesc_data, AARCH64_V0_REGNUM + i,
-				   aarch64_v_register_names[i]);
+	valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data,
+					    AARCH64_V0_REGNUM + i,
+					    aarch64_v_register_names[i]);

       num_regs = AARCH64_V0_REGNUM + i;
+    }
+
+  /* Add the SVE registers.  */
+  if (feature_sve != NULL)
+    {
+      gdb_assert (feature_fpu == NULL);
+      gdb_assert (vg > 0);
+
+      /* Validate the descriptor provides the mandatory sve registers
+	 and allocate their numbers.  */
+      for (i = 0; i < ARRAY_SIZE (aarch64_sve_register_names); i++)
+	valid_p &= tdesc_numbered_register (feature_sve, tdesc_data,
+					    AARCH64_SVE_Z0_REGNUM + i,
+					    aarch64_sve_register_names[i]);
+
+      num_regs = AARCH64_SVE_Z0_REGNUM + i;
+      num_pseudo_regs += 32;	/* add the Vn register pseudos.  */
+    }

+  if (feature_fpu != NULL || feature_sve != NULL)
+    {
       num_pseudo_regs += 32;	/* add the Qn scalar register pseudos */
       num_pseudo_regs += 32;	/* add the Dn scalar register pseudos */
       num_pseudo_regs += 32;	/* add the Sn scalar register pseudos */
@@ -2860,15 +2941,14 @@  aarch64_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
        best_arch != NULL;
        best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
     {
-      /* Found a match.  */
-      break;
-    }
-
-  if (best_arch != NULL)
-    {
-      if (tdesc_data != NULL)
-	tdesc_data_cleanup (tdesc_data);
-      return best_arch->gdbarch;
+      tdep = gdbarch_tdep (best_arch->gdbarch);
+      if (tdep && tdep->vg == vg)
+	{
+	  /* Found a match.  */
+	  if (tdesc_data != NULL)
+	    tdesc_data_cleanup (tdesc_data);
+	  return best_arch->gdbarch;
+	}
     }

   tdep = XCNEW (struct gdbarch_tdep);
@@ -2878,6 +2958,7 @@  aarch64_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
   tdep->lowest_pc = 0x20;
   tdep->jb_pc = -1;		/* Longjump support not enabled by default.  */
   tdep->jb_elt_size = 8;
+  tdep->vg = vg;

   set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h
index 
ef97d1d5f76d80bad1a47c435fa1b7f3cb871d73..217293a4f4e29dccf191508efab1f6a43
9623aac 100644
--- a/gdb/target-descriptions.h
+++ b/gdb/target-descriptions.h
@@ -175,8 +175,8 @@  int tdesc_has_registers (const struct target_desc *);
 /* Return the feature with the given name, if present, or NULL if
    the named feature is not found.  */

-const struct tdesc_feature *tdesc_find_feature (const struct target_desc
*,
-						const char *name);
+struct tdesc_feature *tdesc_find_feature (const struct target_desc *,
+					  const char *name);

 /* Return the name of FEATURE.  */

@@ -253,4 +253,9 @@  void tdesc_create_reg (struct tdesc_feature *feature,
const char *name,
 		       int regnum, int save_restore, const char *group,
 		       int bitsize, const char *type);

+void tdesc_rescale_vector_types (struct tdesc_feature *feature, int
scale);
+
+void tdesc_rescale_registers (struct tdesc_feature *feature, char
*type_name,
+			      int scale);
+
 #endif /* TARGET_DESCRIPTIONS_H */
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 
40f04786b08613d59d94868e5a9c02b9c9df6520..62155a5a94f423ee88af739e3070ff78b
6f75661 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -491,7 +491,7 @@  tdesc_has_registers (const struct target_desc
*target_desc)
 /* Return the feature with the given name, if present, or NULL if
    the named feature is not found.  */

-const struct tdesc_feature *
+struct tdesc_feature *
 tdesc_find_feature (const struct target_desc *target_desc,
 		    const char *name)