* rs6000-tdep: Include "features/rs6000/powerpc-vsx32.c".
[deliverable/binutils-gdb.git] / gdb / ppc-linux-nat.c
index db070b9a7feaf8a14d2e9fc4b98fe5d9f18a15c1..ed197f1163f27ef6d16551fa7dc2a463a0eb747d 100644 (file)
 #define PTRACE_SETVRREGS 19
 #endif
 
+/* PTRACE requests for POWER7 VSX registers.  */
+#ifndef PTRACE_GETVSXREGS
+#define PTRACE_GETVSXREGS 27
+#define PTRACE_SETVSXREGS 28
+#endif
 
 /* Similarly for the ptrace requests for getting / setting the SPE
    registers (ev0 -- ev31, acc, and spefscr).  See the description of
 
 typedef char gdb_vrregset_t[SIZEOF_VRREGS];
 
+/* This is the layout of the POWER7 VSX registers and the way they overlap
+   with the existing FPR and VMX registers.
+
+                    VSR doubleword 0               VSR doubleword 1
+           ----------------------------------------------------------------
+   VSR[0]  |             FPR[0]            |                              |
+           ----------------------------------------------------------------
+   VSR[1]  |             FPR[1]            |                              |
+           ----------------------------------------------------------------
+           |              ...              |                              |
+           |              ...              |                              |
+           ----------------------------------------------------------------
+   VSR[30] |             FPR[30]           |                              |
+           ----------------------------------------------------------------
+   VSR[31] |             FPR[31]           |                              |
+           ----------------------------------------------------------------
+   VSR[32] |                             VR[0]                            |
+           ----------------------------------------------------------------
+   VSR[33] |                             VR[1]                            |
+           ----------------------------------------------------------------
+           |                              ...                             |
+           |                              ...                             |
+           ----------------------------------------------------------------
+   VSR[62] |                             VR[30]                           |
+           ----------------------------------------------------------------
+   VSR[63] |                             VR[31]                           |
+          ----------------------------------------------------------------
+
+   VSX has 64 128bit registers.  The first 32 registers overlap with
+   the FP registers (doubleword 0) and hence extend them with additional
+   64 bits (doubleword 1).  The other 32 regs overlap with the VMX
+   registers.  */
+#define SIZEOF_VSXREGS 32*8
+
+typedef char gdb_vsxregset_t[SIZEOF_VSXREGS];
 
 /* On PPC processors that support the the Signal Processing Extension
    (SPE) APU, the general-purpose registers are 64 bits long.
@@ -152,6 +192,12 @@ struct gdb_evrregset_t
   unsigned long spefscr;
 };
 
+/* Non-zero if our kernel may support the PTRACE_GETVSXREGS and
+   PTRACE_SETVSXREGS requests, for reading and writing the VSX
+   POWER7 registers 0 through 31.  Zero if we've tried one of them and
+   gotten an error.  Note that VSX registers 32 through 63 overlap
+   with VR registers 0 through 31.  */
+int have_ptrace_getsetvsxregs = 1;
 
 /* Non-zero if our kernel may support the PTRACE_GETVRREGS and
    PTRACE_SETVRREGS requests, for reading and writing the Altivec
@@ -239,6 +285,34 @@ ppc_register_u_addr (struct gdbarch *gdbarch, int regno)
   return u_addr;
 }
 
+/* The Linux kernel ptrace interface for POWER7 VSX registers uses the
+   registers set mechanism, as opposed to the interface for all the
+   other registers, that stores/fetches each register individually.  */
+static void
+fetch_vsx_register (struct regcache *regcache, int tid, int regno)
+{
+  int ret;
+  gdb_vsxregset_t regs;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+  ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+  if (ret < 0)
+    {
+      if (errno == EIO)
+       {
+         have_ptrace_getsetvsxregs = 0;
+         return;
+       }
+      perror_with_name (_("Unable to fetch VSX register"));
+    }
+
+  regcache_raw_supply (regcache, regno,
+                      regs + (regno - tdep->ppc_vsr0_upper_regnum)
+                      * vsxregsize);
+}
+
 /* The Linux kernel ptrace interface for AltiVec registers uses the
    registers set mechanism, as opposed to the interface for all the
    other registers, that stores/fetches each register individually.  */
@@ -373,6 +447,14 @@ fetch_register (struct regcache *regcache, int tid, int regno)
         AltiVec registers, fall through and return zeroes, because
         regaddr will be -1 in this case.  */
     }
+  if (vsx_register_p (gdbarch, regno))
+    {
+      if (have_ptrace_getsetvsxregs)
+       {
+         fetch_vsx_register (regcache, tid, regno);
+         return;
+       }
+    }
   else if (spe_register_p (gdbarch, regno))
     {
       fetch_spe_register (regcache, tid, regno);
@@ -428,6 +510,21 @@ fetch_register (struct regcache *regcache, int tid, int regno)
                     gdbarch_byte_order (gdbarch));
 }
 
+static void
+supply_vsxregset (struct regcache *regcache, gdb_vsxregset_t *vsxregsetp)
+{
+  int i;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+  for (i = 0; i < ppc_num_vshrs; i++)
+    {
+       regcache_raw_supply (regcache, tdep->ppc_vsr0_upper_regnum + i,
+                            *vsxregsetp + i * vsxregsize);
+    }
+}
+
 static void
 supply_vrregset (struct regcache *regcache, gdb_vrregset_t *vrregsetp)
 {
@@ -453,6 +550,25 @@ supply_vrregset (struct regcache *regcache, gdb_vrregset_t *vrregsetp)
     }
 }
 
+static void
+fetch_vsx_registers (struct regcache *regcache, int tid)
+{
+  int ret;
+  gdb_vsxregset_t regs;
+
+  ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+  if (ret < 0)
+    {
+      if (errno == EIO)
+       {
+         have_ptrace_getsetvsxregs = 0;
+         return;
+       }
+      perror_with_name (_("Unable to fetch VSX registers"));
+    }
+  supply_vsxregset (regcache, &regs);
+}
+
 static void
 fetch_altivec_registers (struct regcache *regcache, int tid)
 {
@@ -507,6 +623,9 @@ fetch_ppc_registers (struct regcache *regcache, int tid)
   if (have_ptrace_getvrregs)
     if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
       fetch_altivec_registers (regcache, tid);
+  if (have_ptrace_getsetvsxregs)
+    if (tdep->ppc_vsr0_upper_regnum != -1)
+      fetch_vsx_registers (regcache, tid);
   if (tdep->ppc_ev0_upper_regnum >= 0)
     fetch_spe_register (regcache, tid, -1);
 }
@@ -530,6 +649,35 @@ ppc_linux_fetch_inferior_registers (struct regcache *regcache, int regno)
     fetch_register (regcache, tid, regno);
 }
 
+/* Store one VSX register. */
+static void
+store_vsx_register (const struct regcache *regcache, int tid, int regno)
+{
+  int ret;
+  gdb_vsxregset_t regs;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+  ret = ptrace (PTRACE_SETVSXREGS, tid, 0, &regs);
+  if (ret < 0)
+    {
+      if (errno == EIO)
+       {
+         have_ptrace_getsetvsxregs = 0;
+         return;
+       }
+      perror_with_name (_("Unable to fetch VSX register"));
+    }
+
+  regcache_raw_collect (regcache, regno, regs +
+                       (regno - tdep->ppc_vsr0_upper_regnum) * vsxregsize);
+
+  ret = ptrace (PTRACE_SETVSXREGS, tid, 0, &regs);
+  if (ret < 0)
+    perror_with_name (_("Unable to store VSX register"));
+}
+
 /* Store one register. */
 static void
 store_altivec_register (const struct regcache *regcache, int tid, int regno)
@@ -668,6 +816,11 @@ store_register (const struct regcache *regcache, int tid, int regno)
       store_altivec_register (regcache, tid, regno);
       return;
     }
+  if (vsx_register_p (gdbarch, regno))
+    {
+      store_vsx_register (regcache, tid, regno);
+      return;
+    }
   else if (spe_register_p (gdbarch, regno))
     {
       store_spe_register (regcache, tid, regno);
@@ -721,6 +874,19 @@ store_register (const struct regcache *regcache, int tid, int regno)
     }
 }
 
+static void
+fill_vsxregset (const struct regcache *regcache, gdb_vsxregset_t *vsxregsetp)
+{
+  int i;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+  for (i = 0; i < ppc_num_vshrs; i++)
+    regcache_raw_collect (regcache, tdep->ppc_vsr0_upper_regnum + i,
+                         *vsxregsetp + i * vsxregsize);
+}
+
 static void
 fill_vrregset (const struct regcache *regcache, gdb_vrregset_t *vrregsetp)
 {
@@ -744,6 +910,29 @@ fill_vrregset (const struct regcache *regcache, gdb_vrregset_t *vrregsetp)
     }
 }
 
+static void
+store_vsx_registers (const struct regcache *regcache, int tid)
+{
+  int ret;
+  gdb_vsxregset_t regs;
+
+  ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+  if (ret < 0)
+    {
+      if (errno == EIO)
+       {
+         have_ptrace_getsetvsxregs = 0;
+         return;
+       }
+      perror_with_name (_("Couldn't get VSX registers"));
+    }
+
+  fill_vsxregset (regcache, &regs);
+
+  if (ptrace (PTRACE_SETVSXREGS, tid, 0, &regs) < 0)
+    perror_with_name (_("Couldn't write VSX registers"));
+}
+
 static void
 store_altivec_registers (const struct regcache *regcache, int tid)
 {
@@ -802,6 +991,9 @@ store_ppc_registers (const struct regcache *regcache, int tid)
   if (have_ptrace_getvrregs)
     if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
       store_altivec_registers (regcache, tid);
+  if (have_ptrace_getsetvsxregs)
+    if (tdep->ppc_vsr0_upper_regnum != -1)
+      store_vsx_registers (regcache, tid);
   if (tdep->ppc_ev0_upper_regnum >= 0)
     store_spe_register (regcache, tid, -1);
 }
@@ -1037,6 +1229,7 @@ static const struct target_desc *
 ppc_linux_read_description (struct target_ops *ops)
 {
   int altivec = 0;
+  int vsx = 0;
 
   int tid = TIDGET (inferior_ptid);
   if (tid == 0)
@@ -1055,6 +1248,19 @@ ppc_linux_read_description (struct target_ops *ops)
        perror_with_name (_("Unable to fetch SPE registers"));
     }
 
+  if (have_ptrace_getsetvsxregs)
+    {
+      gdb_vsxregset_t vsxregset;
+
+      if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0)
+       vsx = 1;
+
+      /* EIO means that the PTRACE_GETVSXREGS request isn't supported.
+        Anything else needs to be reported.  */
+      else if (errno != EIO)
+       perror_with_name (_("Unable to fetch VSX registers"));
+    }
+
   if (have_ptrace_getvrregs)
     {
       gdb_vrregset_t vrregset;
@@ -1076,11 +1282,23 @@ ppc_linux_read_description (struct target_ops *ops)
     errno = 0;
     msr = (long) ptrace (PTRACE_PEEKUSER, tid, PT_MSR * 8, 0);
     if (errno == 0 && msr < 0)
-      return altivec? tdesc_powerpc_altivec64l : tdesc_powerpc_64l;
+      {
+       if (vsx)
+         return tdesc_powerpc_vsx64l;
+       else if (altivec)
+         return tdesc_powerpc_altivec64l;
+
+       return tdesc_powerpc_64l;
+      }
   }
 #endif
 
-  return altivec? tdesc_powerpc_altivec32l : tdesc_powerpc_32l;
+  if (vsx)
+    return tdesc_powerpc_vsx32l;
+  else if (altivec)
+    return tdesc_powerpc_altivec32l;
+
+  return tdesc_powerpc_32l;
 }
 
 void _initialize_ppc_linux_nat (void);
This page took 0.040468 seconds and 4 git commands to generate.