diff mbox

[04/24] Add MIPS Config5 register related support

Message ID 1467038991-6600-4-git-send-email-bhushan.attarde@imgtec.com
State New
Headers show

Commit Message

Bhushan Attarde June 27, 2016, 2:49 p.m. UTC
This patch has below changes:

    1. Add a Config5 register binding in order to get access to the FRE
    floating point mode bit. The internal register number is allocated
    dynamically only when provided by the target description, and is tracked in
    struct mips_regnum.

    2. MIPS native: Use FP regset to support Config5
    Use the NT_FPREGSET register set in preference to PTRACE_GETFPREGS for
    getting the floating point registers. This register set also provides the
    Config5 register which PTRACE_GETFPREGS doesn't.

    3. Define type for the MIPS CP0 Config5 register, allowing the important
    fields to be decoded.

    gdb/ChangeLog:

    	* mips-tdep.c (mips_config5_type): New function.
        (mips_linux_reg_names): Add config5.
    	(mips_gdbarch_init): Initialise config5 register number and bind
    	to named config5 register in target description.
        (mips_value_to_register): Return config5_type for config5 register.
    	* mips-tdep.h (struct mips_regnum): New config5 field.
        (gdbarch_tdep): New config5_type field.
    	* mips-linux-nat.c: Include "elf/common.h".
    	(PTRACE_GETREGSET, PTRACE_SETREGSET): New fallback definitions.
    	(have_ptrace_getregset_gp, have_ptrace_getregset_fp): New variables.
    	(mips64_linux_regsets_fetch_registers): Use PTRACE_GETREGSET with
    	NT_FPREGSET in preferance to PTRACE_GETFPREGS to get FP and
    	Config5 registers.
    	(mips64_linux_regsets_store_registers): Use PTRACE_SETREGSET with
    	NT_FPREGSET in preferance to PTRACE_SETFPREGS to get FP and
    	Config5 registers
---
 gdb/mips-linux-nat.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++-----
 gdb/mips-tdep.c      |  35 ++++++++
 gdb/mips-tdep.h      |   4 +
 3 files changed, 239 insertions(+), 21 deletions(-)
diff mbox

Patch

diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c
index bfe9fcb..627c652 100644
--- a/gdb/mips-linux-nat.c
+++ b/gdb/mips-linux-nat.c
@@ -27,6 +27,7 @@ 
 #include "linux-nat.h"
 #include "mips-linux-tdep.h"
 #include "target-descriptions.h"
+#include "elf/common.h"
 
 #include "gdb_proc_service.h"
 #include "gregset.h"
@@ -46,10 +47,22 @@ 
 #define PTRACE_GET_THREAD_AREA 25
 #endif
 
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET	0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET	0x4205
+#endif
+
 /* Assume that we have PTRACE_GETREGS et al. support.  If we do not,
    we'll clear this and use PTRACE_PEEKUSER instead.  */
 static int have_ptrace_regsets = 1;
 
+/* Does the current host support PTRACE_GETREGSET?  */
+static int have_ptrace_getregset_gp = 1;
+static int have_ptrace_getregset_fp = 1;
+
 /* Saved function pointers to fetch and store a single register using
    PTRACE_PEEKUSER and PTRACE_POKEUSER.  */
 
@@ -217,18 +230,25 @@  mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 				      struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
   int is_fp, is_dsp;
   int have_dsp;
   int regi;
   int tid;
 
-  if (regno >= mips_regnum (gdbarch)->fp0
-      && regno <= mips_regnum (gdbarch)->fp0 + 32)
+  /* FP registers can be obtained in several ways.
+     is_fp will be cleared once the registers have been obtained.  */
+  if (regno == -1)
+    is_fp = 1;
+  else if (regno >= mips_regnum (gdbarch)->fp0
+	   && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_control_status)
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
+  else if (regno == mips_regnum (gdbarch)->config5)
+    is_fp = 1;
   else
     is_fp = 0;
 
@@ -266,23 +286,93 @@  mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 			     (const mips64_elf_gregset_t *) &regs);
     }
 
-  if (regno == -1 || is_fp)
+  if (is_fp)
     {
-      mips64_elf_fpregset_t fp_regs;
+      const struct mips_regnum *rn = mips_regnum (gdbarch);
+      int float_regnum = rn->fp0;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+      /* Try the FP regset next as it may contain Config5 */
+      if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
 	{
-	  if (errno == EIO)
+	  unsigned char fp_regs[34][8];
+	  struct iovec iovec;
+	  int ret;
+
+	  iovec.iov_base = &fp_regs;
+	  iovec.iov_len = sizeof (fp_regs);
+
+	  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+	  if (ret < 0)
 	    {
-	      have_ptrace_regsets = 0;
-	      return;
+	      if (errno == EIO)
+		have_ptrace_getregset_gp = 0;
+	      else if (errno == EINVAL)
+		have_ptrace_getregset_fp = 0;
+	      else
+		perror_with_name (_("Unable to fetch FP registers."));
+	    }
+	  else
+	    {
+	      for (regi = 0; regi < 32; regi++)
+		{
+		  if (register_size (gdbarch, float_regnum + regi) == 8)
+		    {
+		      /* FR = 1
+			 copy entire double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi]);
+		    }
+		  else if (regi & 1)
+		    {
+		      /* FR = 0
+			 odd single from top of even double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi - 1] +
+					   4 - 4*big_endian);
+		    }
+		  else
+		    {
+		      /* FR = 0
+			 even single from bottom of even double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi] + 4 * big_endian);
+		    }
+		}
+
+	      if (iovec.iov_len >= 32*8 + 4)
+		regcache_raw_supply (regcache, rn->fp_control_status,
+				     (char *) (fp_regs + 32) + 0);
+	      if (iovec.iov_len >= 32*8 + 8)
+		regcache_raw_supply (regcache, rn->fp_implementation_revision,
+				     (char *) (fp_regs + 32) + 4);
+	      if (iovec.iov_len >= 33*8 + 4 && rn->config5 >= 0)
+		regcache_raw_supply (regcache, rn->config5,
+				     (char *) (fp_regs + 33) + 0);
+
+	      /* we've got fp registers now */
+	      is_fp = 0;
 	    }
-	  perror_with_name (_("Couldn't get FP registers"));
 	}
 
-      mips64_supply_fpregset (regcache,
-			      (const mips64_elf_fpregset_t *) &fp_regs);
+      /* Fall back to GETFPREGS. */
+      if (is_fp)
+	{
+	  mips64_elf_fpregset_t fp_regs;
+
+	  if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    {
+	      if (errno == EIO)
+		{
+		  have_ptrace_regsets = 0;
+		  return;
+		}
+	      perror_with_name (_("Couldn't get FP registers"));
+	    }
+
+	  mips64_supply_fpregset (regcache,
+				  (const mips64_elf_fpregset_t *) &fp_regs);
+	}
     }
 
   if (is_dsp)
@@ -305,11 +395,16 @@  mips64_linux_regsets_store_registers (struct target_ops *ops,
 				      struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
   int is_fp, is_dsp;
   int have_dsp;
   int regi;
   int tid;
 
+  /* FP registers can be written in several ways.
+     is_fp will be cleared once the registers have been written.  */
+  if (regno == -1)
+    is_fp = 1;
   if (regno >= mips_regnum (gdbarch)->fp0
       && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
@@ -317,6 +412,8 @@  mips64_linux_regsets_store_registers (struct target_ops *ops,
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
+  else if (regno == mips_regnum (gdbarch)->config5)
+    is_fp = 1;
   else
     is_fp = 0;
 
@@ -349,19 +446,101 @@  mips64_linux_regsets_store_registers (struct target_ops *ops,
 	perror_with_name (_("Couldn't set registers"));
     }
 
-  if (regno == -1 || is_fp)
+  if (is_fp)
     {
-      mips64_elf_fpregset_t fp_regs;
+      const struct mips_regnum *rn = mips_regnum (gdbarch);
+      int float_regnum = rn->fp0;
+
+      /* Try the FP regset next as it may contain Config5 */
+      if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
+	{
+	  unsigned char fp_regs[34][8];
+	  struct iovec iovec;
+	  int ret;
+
+	  iovec.iov_base = &fp_regs;
+	  iovec.iov_len = sizeof (fp_regs);
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+	  if (ret < 0)
+	    {
+	      if (errno == EIO)
+		have_ptrace_getregset_gp = 0;
+	      else if (errno == EINVAL)
+		have_ptrace_getregset_fp = 0;
+	      else
+		perror_with_name (_("Unable to fetch FP registers."));
+	    }
+	  else
+	    {
+	      for (regi = 0; regi < 32; regi++)
+		{
+		  if (register_size (gdbarch, float_regnum + regi) == 8)
+		    {
+		      /* FR = 1
+			 copy entire double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi]);
+		    }
+		  else if (regi & 1)
+		    {
+		      /* FR = 0
+			 odd single from top of even double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi - 1] +
+					   4 - 4*big_endian);
+		    }
+		  else
+		    {
+		      /* FR = 0
+			 even single from bottom of even double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi] + 4 * big_endian);
+		    }
+		}
+
+	      regcache_raw_collect (regcache, rn->fp_control_status,
+				   (char *) (fp_regs + 32) + 0);
+	      regcache_raw_collect (regcache, rn->fp_implementation_revision,
+				   (char *) (fp_regs + 32) + 4);
+	      if (rn->config5 >= 0)
+		regcache_raw_collect (regcache, rn->config5,
+				      (char *) (fp_regs + 33) + 0);
+
+	      /* don't modify iovec length from amount of data returned */
+	      ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+	      if (ret < 0)
+		{
+		  if (errno == EIO)
+		    have_ptrace_getregset_gp = 0;
+		  else if (errno == EINVAL)
+		    have_ptrace_getregset_fp = 0;
+		  else
+		    perror_with_name (_("Unable to store FP/MSA registers."));
+		}
+	      else
+		{
+		  /* we've got fp registers now */
+		  is_fp = 0;
+		}
+	    }
+	}
 
-      mips64_fill_fpregset (regcache, &fp_regs, regno);
+      /* Fall back to SETFPREGS. */
+      if (is_fp)
+	{
+	  mips64_elf_fpregset_t fp_regs;
+
+	  if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    perror_with_name (_("Couldn't get FP registers"));
 
-      if (ptrace (PTRACE_SETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't set FP registers"));
+	  mips64_fill_fpregset (regcache, &fp_regs, regno);
+
+	  if (ptrace (PTRACE_SETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    perror_with_name (_("Couldn't set FP registers"));
+	}
     }
 
   if (is_dsp)
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index ae4dc2e..28cfb57 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -993,6 +993,25 @@  mips_register_to_value (struct frame_info *frame, int regnum,
     }
 }
 
+static struct type *
+mips_config5_type (struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (tdep->config5_type == NULL)
+    {
+      struct type *t;
+      /* top half has flags */
+      t = arch_flags_type (gdbarch, "__gdb_builtin_type_config5", 4);
+      append_flags_type_flag (t, 8, "FRE");
+
+      TYPE_NAME (t) = "config5";
+      tdep->config5_type = t;
+    }
+
+  return tdep->config5_type;
+}
+
 static void
 mips_value_to_register (struct frame_info *frame, int regnum,
 			struct type *type, const gdb_byte *from)
@@ -1080,6 +1099,8 @@  mips_register_type (struct gdbarch *gdbarch, int regnum)
       else if (rawnum == mips_regnum (gdbarch)->fp_control_status
 	  || rawnum == mips_regnum (gdbarch)->fp_implementation_revision)
 	return builtin_type (gdbarch)->builtin_int32;
+      else if (rawnum == mips_regnum (gdbarch)->config5)
+	return mips_config5_type (gdbarch);
       else if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
 	       && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
 	       && rawnum >= MIPS_FIRST_EMBED_REGNUM
@@ -1152,6 +1173,9 @@  mips_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
 	      && rawnum < mips_regnum (gdbarch)->dspacc + 6)))
     return builtin_type (gdbarch)->builtin_int32;
 
+  if (rawnum == mips_regnum (gdbarch)->config5)
+    return mips_config5_type (gdbarch);
+
   if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
       && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
       && rawnum >= MIPS_EMBED_FP0_REGNUM + 32
@@ -8236,6 +8260,7 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     info.tdep_info = &tdep_info;
 
   /* Fill in the OS dependent register numbers and names.  */
+  mips_regnum.config5 = -1;
   if (info.osabi == GDB_OSABI_IRIX)
     {
       mips_regnum.fp0 = 32;
@@ -8350,6 +8375,15 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       valid_p &= tdesc_numbered_register (feature, tdesc_data,
 					  mips_regnum.cause, "cause");
 
+      /* Optionally, Config5 contains FP mode bits */
+      if (tdesc_unnumbered_register (feature, "config5"))
+	{
+	  /* Allocate a new register.  */
+	  mips_regnum.config5 = num_regs++;
+	  tdesc_numbered_register (feature, tdesc_data,
+				   mips_regnum.config5, "config5");
+	}
+
       if (!valid_p)
 	{
 	  tdesc_data_cleanup (tdesc_data);
@@ -8666,6 +8700,7 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->register_size = 0;
   tdep->fp_register_size = info.tdep_info->fp_register_size;
   tdep->fp_register_size_fixed_p = 0;
+  tdep->config5_type = NULL;
 
   if (info.target_desc)
     {
diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h
index 6c1d9ca..39af72b 100644
--- a/gdb/mips-tdep.h
+++ b/gdb/mips-tdep.h
@@ -63,6 +63,7 @@  struct mips_regnum
   int fp0;
   int fp_implementation_revision;
   int fp_control_status;
+  int config5;
   int badvaddr;		/* Bad vaddr for addressing exception.  */
   int cause;		/* Describes last exception.  */
   int hi;		/* Multiply/divide temp.  */
@@ -119,6 +120,9 @@  struct gdbarch_tdep
   int fp_register_size_fixed_p;
   int fp_register_size;
 
+  /* ISA-specific data types.  */
+  struct type *config5_type;
+
   /* Return the expected next PC if FRAME is stopped at a syscall
      instruction.  */
   CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);