This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add support for accessing VFP registers to ARM native Linux


All,

The attached patch adds support for VFP registers to GDB running
natively under ARM Linux.  The patch is based in large part on the
equivalent functionality in gdbserver for remote debugging of ARM Linux.

I have tested it with arm-unknown-linux-gnueabi on a target with Neon,
one with VFPv3-D16, one with VFPv2, and a target without VFP.  I have
also tested it on arm-none-linux on a target without VFP.  The patch has
not been tested on an XScale as I do not have access to an appropriate
device.

Can someone please review the patch, comment, and if appropriate commit
it?

Proposed ChangeLog: 

2010-03-01  Matthew Gretton-Dann  <matthew.gretton-dann@arm.com>

	* arm-tdep.h (gdb_regnum): Add ARM_FPSCR_REGNUM
	* arm-linux-nat.c (arm_linux_vfp_register_count): Add new
	variable.
	(fetch_vfp_registers): New function to fetch VFP registers.
	(store_vfp_registers): New function to store VFP registers.
	(arm_linux_fetch_inferior_registers): Add support for VFP
	registers.
	(arm_linux_store_inferior_registers): Likewise.
	(arm_linux_read_description): Likewise.
	(arm_read_auxv, arm_get_hwcap): New function.
	(_initialize_arm_linux_nat): Delay initialising iWMMX tdesc
	until we need it.

Thanks,

Matt

-- 
Matthew Gretton-Dann
Principal Engineer - Tools, PD Software
ARM Limited
Index: gdb/arm-linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/arm-linux-nat.c,v
retrieving revision 1.42
diff -u -p -r1.42 arm-linux-nat.c
--- gdb/arm-linux-nat.c	1 Jan 2010 07:31:29 -0000	1.42
+++ gdb/arm-linux-nat.c	1 Mar 2010 11:33:32 -0000
@@ -29,6 +29,7 @@
 #include "arm-tdep.h"
 #include "arm-linux-tdep.h"
 
+#include <elf/common.h>
 #include <sys/user.h>
 #include <sys/ptrace.h>
 #include <sys/utsname.h>
@@ -41,6 +42,9 @@
 #include "gdb_proc_service.h"
 
 #include "features/arm-with-iwmmxt.c"
+#include "features/arm-with-vfpv2.c"
+#include "features/arm-with-vfpv3.c"
+#include "features/arm-with-neon.c"
 
 #ifndef PTRACE_GET_THREAD_AREA
 #define PTRACE_GET_THREAD_AREA 22
@@ -51,9 +55,25 @@
 #define PTRACE_SETWMMXREGS 19
 #endif
 
+#ifndef PTRACE_GETVFPREGS
+#define PTRACE_GETVFPREGS 27
+#define PTRACE_SETVFPREGS 28
+#endif
+
+/* These are in <asm/elf.h> in current kernels.  */
+#define HWCAP_VFP       64
+#define HWCAP_IWMMXT    512
+#define HWCAP_NEON      4096
+#define HWCAP_VFPv3     8192
+#define HWCAP_VFPv3D16  16384
+
 /* A flag for whether the WMMX registers are available.  */
 static int arm_linux_has_wmmx_registers;
 
+/* The number of 64-bit VFP registers we have (expect this to be 0, 16, or
+   32). */
+static int arm_linux_vfp_register_count;
+
 extern int arm_apcs_32;
 
 /* The following variables are used to determine the version of the
@@ -447,6 +467,67 @@ store_wmmx_regs (const struct regcache *
     }
 }
 
+/* Fetch and store VFP Registers.  The kernel object has space for 32
+   64-bit registers, and the FPSCR.  This is even when on a VFPv2 or
+   VFPv3D16 target.  */
+#define VFP_REGS_SIZE (32 * 8 + 4)
+
+static void
+fetch_vfp_regs (struct regcache *regcache)
+{
+  char regbuf[VFP_REGS_SIZE];
+  int ret, regno, tid;
+
+  /* Get the thread id for the ptrace call.  */
+  tid = GET_THREAD_ID (inferior_ptid);
+
+  ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf);
+  if (ret < 0)
+    {
+      warning (_("Unable to fetch VFPv3 registers."));
+      return;
+    }
+
+  for (regno = 0; regno < arm_linux_vfp_register_count; regno++)
+    regcache_raw_supply (regcache, regno + ARM_D0_REGNUM,
+			 (char *) regbuf + regno * 8);
+
+  regcache_raw_supply (regcache, ARM_FPSCR_REGNUM,
+		       (char *) regbuf + 32 * 8);
+}
+
+static void
+store_vfp_regs (const struct regcache *regcache)
+{
+  char regbuf[VFP_REGS_SIZE];
+  int ret, regno, tid;
+
+  /* Get the thread id for the ptrace call.  */
+  tid = GET_THREAD_ID (inferior_ptid);
+
+  ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf);
+  if (ret < 0)
+    {
+      warning (_("Unable to fetch VFPv3 registers."));
+      return;
+    }
+
+  for (regno = 0; regno < arm_linux_vfp_register_count; regno++)
+    regcache_raw_collect (regcache, regno + ARM_D0_REGNUM,
+			  (char *) regbuf + regno * 8);
+
+  regcache_raw_collect (regcache, ARM_FPSCR_REGNUM,
+			(char *) regbuf + 32 * 8);
+
+  ret = ptrace (PTRACE_SETVFPREGS, tid, 0, regbuf);
+
+  if (ret < 0)
+    {
+      warning (_("Unable to store VFPv3 registers."));
+      return;
+    }
+}
+
 /* Fetch registers from the child process.  Fetch all registers if
    regno == -1, otherwise fetch all general registers or all floating
    point registers depending upon the value of regno.  */
@@ -461,6 +542,8 @@ arm_linux_fetch_inferior_registers (stru
       fetch_fpregs (regcache);
       if (arm_linux_has_wmmx_registers)
 	fetch_wmmx_regs (regcache);
+      if (arm_linux_vfp_register_count > 0)
+	fetch_vfp_regs (regcache);
     }
   else 
     {
@@ -471,6 +554,10 @@ arm_linux_fetch_inferior_registers (stru
       else if (arm_linux_has_wmmx_registers
 	       && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM)
 	fetch_wmmx_regs (regcache);
+      else if (arm_linux_vfp_register_count > 0
+	       && regno >= ARM_D0_REGNUM
+	       && regno <= ARM_D0_REGNUM + arm_linux_vfp_register_count)
+	fetch_vfp_regs (regcache);
     }
 }
 
@@ -488,6 +575,8 @@ arm_linux_store_inferior_registers (stru
       store_fpregs (regcache);
       if (arm_linux_has_wmmx_registers)
 	store_wmmx_regs (regcache);
+      if (arm_linux_vfp_register_count > 0)
+	store_vfp_regs (regcache);
     }
   else
     {
@@ -498,6 +587,10 @@ arm_linux_store_inferior_registers (stru
       else if (arm_linux_has_wmmx_registers
 	       && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM)
 	store_wmmx_regs (regcache);
+      else if (arm_linux_vfp_register_count > 0
+	       && regno >= ARM_D0_REGNUM
+	       && regno <= ARM_D0_REGNUM + arm_linux_vfp_register_count)
+	store_vfp_regs (regcache);
     }
 }
 
@@ -575,23 +668,118 @@ get_linux_version (unsigned int *vmajor,
   return ((*vmajor << 16) | (*vminor << 8) | *vrelease);
 }
 
+/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
+   to debugger memory starting at MYADDR.  */
+
+static int
+arm_read_auxv(int offset, unsigned char *myaddr, unsigned int len)
+{
+  char filename[PATH_MAX];
+  int fd, n;
+  int pid = GET_LWP (inferior_ptid);
+
+  snprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
+
+  fd = open (filename, O_RDONLY);
+  if (fd < 0)
+    return -1;
+
+  if (offset != (CORE_ADDR) 0
+      && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+    n = -1;
+  else
+    n = read (fd, myaddr, len);
+
+  close (fd);
+
+  return n;
+}
+
+static int
+arm_get_hwcap (unsigned long *valp)
+{
+  unsigned char *data = alloca (8);
+  int offset = 0;
+
+  while (arm_read_auxv (offset, data, 8) == 8)
+    {
+      unsigned int *data_p = (unsigned int *)data;
+      if (data_p[0] == AT_HWCAP)
+	{
+	  *valp = data_p[1];
+	  return 1;
+	}
+
+      offset += 8;
+    }
+
+  *valp = 0;
+  return 0;
+}
+
 static const struct target_desc *
 arm_linux_read_description (struct target_ops *ops)
 {
-  int ret;
-  char regbuf[IWMMXT_REGS_SIZE];
+  unsigned long arm_hwcap = 0;
+  arm_linux_has_wmmx_registers = 0;
+  arm_linux_vfp_register_count = 0;
+
+  if (arm_get_hwcap (&arm_hwcap) == 0)
+    {
+      return NULL;
+    }
+
+  if (arm_hwcap & HWCAP_IWMMXT)
+    {
+      arm_linux_has_wmmx_registers = 1;
+      if (tdesc_arm_with_iwmmxt == NULL)
+	initialize_tdesc_arm_with_iwmmxt ();
+      return tdesc_arm_with_iwmmxt;
+    }
+
+  if (arm_hwcap & HWCAP_VFP)
+    {
+      int pid;
+      char *buf;
+      const struct target_desc * result = NULL;
+
+      /* NEON implies VFPv3-D32 or no-VFP unit.  Say that we only support
+	 Neon with VFPv3-D32.  */
+      if (arm_hwcap & HWCAP_NEON)
+	{
+	  arm_linux_vfp_register_count = 32;
+	  if (tdesc_arm_with_neon == NULL)
+	    initialize_tdesc_arm_with_neon ();
+	  result = tdesc_arm_with_neon;
+	}
+      else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+	{
+	  arm_linux_vfp_register_count = 32;
+	  if (tdesc_arm_with_vfpv3 == NULL)
+	    initialize_tdesc_arm_with_vfpv3 ();
+	  result = tdesc_arm_with_vfpv3;
+	}
+      else
+	{
+	  arm_linux_vfp_register_count = 16;
+	  if (tdesc_arm_with_vfpv2 == NULL)
+	    initialize_tdesc_arm_with_vfpv2 ();
+	  result = tdesc_arm_with_vfpv2;
+	}
+
+      /* Now make sure that the kernel supports reading these
+	 registers.  Support was added in 2.6.30.  */
+      pid = GET_LWP (inferior_ptid);
+      errno = 0;
+      buf = alloca (VFP_REGS_SIZE);
+      if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
+	  && errno == EIO)
+	result = NULL;
 
-  ret = ptrace (PTRACE_GETWMMXREGS, GET_THREAD_ID (inferior_ptid),
-		0, regbuf);
-  if (ret < 0)
-    arm_linux_has_wmmx_registers = 0;
-  else
-    arm_linux_has_wmmx_registers = 1;
+      return result;
+    }
 
-  if (arm_linux_has_wmmx_registers)
-    return tdesc_arm_with_iwmmxt;
-  else
-    return NULL;
+  return NULL;
 }
 
 void _initialize_arm_linux_nat (void);
@@ -614,7 +802,4 @@ _initialize_arm_linux_nat (void)
 
   /* Register the target.  */
   linux_nat_add_target (t);
-
-  /* Initialize the standard target descriptions.  */
-  initialize_tdesc_arm_with_iwmmxt ();
 }
Index: gdb/arm-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/arm-tdep.h,v
retrieving revision 1.39
diff -u -p -r1.39 arm-tdep.h
--- gdb/arm-tdep.h	1 Feb 2010 16:13:15 -0000	1.39
+++ gdb/arm-tdep.h	1 Mar 2010 11:33:32 -0000
@@ -50,6 +50,7 @@ enum gdb_regnum {
   ARM_WCGR7_REGNUM = ARM_WCGR0_REGNUM + 7,
   ARM_D0_REGNUM,		/* VFP double-precision registers.  */
   ARM_D31_REGNUM = ARM_D0_REGNUM + 31,
+  ARM_FPSCR_REGNUM,
 
   ARM_NUM_REGS,
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]