From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18183 invoked by alias); 15 Mar 2010 14:49:06 -0000 Received: (qmail 18157 invoked by uid 22791); 15 Mar 2010 14:49:03 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from cam-admin0.cambridge.arm.com (HELO cam-admin0.cambridge.arm.com) (217.140.96.50) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 15 Mar 2010 14:48:57 +0000 Received: from cam-owa2.Emea.Arm.com (cam-owa2.emea.arm.com [10.1.105.18]) by cam-admin0.cambridge.arm.com (8.12.6/8.12.6) with ESMTP id o2FEmteI011102 for ; Mon, 15 Mar 2010 14:48:55 GMT Received: from [10.1.79.63] ([10.1.255.212]) by cam-owa2.Emea.Arm.com with Microsoft SMTPSVC(6.0.3790.3959); Mon, 15 Mar 2010 14:48:54 +0000 Subject: [PING][PATCH] Add support for accessing VFP registers to ARM native Linux From: Matthew Gretton-Dann To: gdb-patches@sourceware.org Content-Type: multipart/mixed; boundary="=-QwhX8+u5J8RPokw9YxV9" Date: Mon, 15 Mar 2010 14:49:00 -0000 Message-Id: <1268664534.12300.0.camel@e102111-lin.cambridge.arm.com> Mime-Version: 1.0 X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-03/txt/msg00539.txt.bz2 --=-QwhX8+u5J8RPokw9YxV9 Content-Type: text/plain Content-Transfer-Encoding: 7bit Content-length: 1370 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 * 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 -- Matthew Gretton-Dann Principal Engineer - Tools, PD Software ARM Limited --=-QwhX8+u5J8RPokw9YxV9 Content-Disposition: attachment; filename=1002-gdb-vfp-native.patch Content-Type: text/x-patch; name=1002-gdb-vfp-native.patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-length: 8471 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 #include #include #include @@ -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 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, --=-QwhX8+u5J8RPokw9YxV9--