/* GNU/Linux on ARM native support.
- Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "inferior.h"
#include "target.h"
#include "linux-nat.h"
#include "target-descriptions.h"
-#include "xml-support.h"
+#include "auxv.h"
#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>
/* Defines ps_err_e, struct ps_prochandle. */
#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
#endif
#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
tid = PIDGET (ptid);
return tid;
}
+
#define GET_THREAD_ID(PTID) get_thread_id (PTID)
/* Get the value of a particular register from the floating point
{
if (arm_apcs_32)
regcache_raw_supply (regcache, ARM_PS_REGNUM,
- (char *) ®s[ARM_CPSR_REGNUM]);
+ (char *) ®s[ARM_CPSR_GREGNUM]);
else
regcache_raw_supply (regcache, ARM_PS_REGNUM,
(char *) ®s[ARM_PC_REGNUM]);
if (ARM_PC_REGNUM == regno)
{
- regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]);
+ regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove
+ (get_regcache_arch (regcache),
+ regs[ARM_PC_REGNUM]);
regcache_raw_supply (regcache, ARM_PC_REGNUM,
(char *) ®s[ARM_PC_REGNUM]);
}
if (arm_apcs_32)
regcache_raw_supply (regcache, ARM_PS_REGNUM,
- (char *) ®s[ARM_CPSR_REGNUM]);
+ (char *) ®s[ARM_CPSR_GREGNUM]);
else
regcache_raw_supply (regcache, ARM_PS_REGNUM,
(char *) ®s[ARM_PC_REGNUM]);
- regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]);
+ regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove
+ (get_regcache_arch (regcache), regs[ARM_PC_REGNUM]);
regcache_raw_supply (regcache, ARM_PC_REGNUM,
(char *) ®s[ARM_PC_REGNUM]);
}
regcache_raw_collect (regcache, regno, (char *) ®s[regno]);
else if (arm_apcs_32 && regno == ARM_PS_REGNUM)
regcache_raw_collect (regcache, regno,
- (char *) ®s[ARM_CPSR_REGNUM]);
+ (char *) ®s[ARM_CPSR_GREGNUM]);
else if (!arm_apcs_32 && regno == ARM_PS_REGNUM)
regcache_raw_collect (regcache, ARM_PC_REGNUM,
(char *) ®s[ARM_PC_REGNUM]);
if (arm_apcs_32 && regcache_valid_p (regcache, ARM_PS_REGNUM))
regcache_raw_collect (regcache, ARM_PS_REGNUM,
- (char *) ®s[ARM_CPSR_REGNUM]);
+ (char *) ®s[ARM_CPSR_GREGNUM]);
ret = ptrace (PTRACE_SETREGS, tid, 0, ®s);
}
}
+/* 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 VFP 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 VFP registers (for update)."));
+ 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 VFP 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. */
static void
-arm_linux_fetch_inferior_registers (struct regcache *regcache, int regno)
+arm_linux_fetch_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
{
if (-1 == regno)
{
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
{
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);
}
}
point registers depending upon the value of regno. */
static void
-arm_linux_store_inferior_registers (struct regcache *regcache, int regno)
+arm_linux_store_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
{
if (-1 == regno)
{
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
{
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);
}
}
return ((*vmajor << 16) | (*vminor << 8) | *vrelease);
}
-static LONGEST (*super_xfer_partial) (struct target_ops *, enum target_object,
- const char *, gdb_byte *, const gdb_byte *,
- ULONGEST, LONGEST);
-
-static LONGEST
-arm_linux_xfer_partial (struct target_ops *ops,
- enum target_object object,
- const char *annex,
- gdb_byte *readbuf, const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len)
+static const struct target_desc *
+arm_linux_read_description (struct target_ops *ops)
{
- if (object == TARGET_OBJECT_AVAILABLE_FEATURES)
+ CORE_ADDR arm_hwcap = 0;
+ arm_linux_has_wmmx_registers = 0;
+ arm_linux_vfp_register_count = 0;
+
+ if (target_auxv_search (ops, AT_HWCAP, &arm_hwcap) != 1)
+ {
+ 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)
{
- if (annex != NULL && strcmp (annex, "target.xml") == 0)
+ 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)
{
- int ret;
- char regbuf[IWMMXT_REGS_SIZE];
-
- 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;
-
- if (arm_linux_has_wmmx_registers)
- annex = "arm-with-iwmmxt.xml";
- else
- return -1;
+ 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;
- return xml_builtin_xfer_partial (annex, readbuf, writebuf, offset, len);
+ return result;
}
- return super_xfer_partial (ops, object, annex, readbuf, writebuf,
- offset, len);
+ return NULL;
}
void _initialize_arm_linux_nat (void);
t->to_fetch_registers = arm_linux_fetch_inferior_registers;
t->to_store_registers = arm_linux_store_inferior_registers;
- /* Override the default to_xfer_partial. */
- super_xfer_partial = t->to_xfer_partial;
- t->to_xfer_partial = arm_linux_xfer_partial;
+ t->to_read_description = arm_linux_read_description;
/* Register the target. */
linux_nat_add_target (t);